DESeq2 - Analysis of each Sensitive/Resistant pair
for (pair in sensitive_resistant_pairs) {
split <- strsplit(pair, "_vs_")
cont <- split[[1]][2]
exp <- split[[1]][1]
# Creates another notebook that shares the same environment
outFile <- str_interp("generated-notebooks/deseq-analysis-${pair}.html")
print(str_interp("Creating notebook for ${exp} vs ${cont}"))
rmarkdown::render('deseq/single-cellline-vs-control.Rmd',
output_file = outFile,
params = list(controlCellLine = cont,
experimentalCellLine = exp,
countmatrix.all = countmatrix.all,
metadata.all = metadata.all))
}
Combine results across pairs
Combine differential gene data
full_results_genes <- data.frame()
for (pair in sensitive_resistant_pairs) {
pair_results_file <- str_interp("deseq/output/${pair}_deseq_results.csv")
pair_results <- as.data.frame(read.csv(pair_results_file, sep = ",", header = TRUE, row.names = 1))
pair_results_formatted <- pair_results[, c("padj", "log2FoldChange")]
colnames(pair_results_formatted) <- c(str_interp("${pair}_padj"), str_interp("${pair}_l2fc"))
pair_results_formatted$gene <- rownames(pair_results_formatted)
if (ncol(full_results_genes) == 0) {
full_results_genes = pair_results_formatted
} else {
full_results_genes <- merge(full_results_genes, pair_results_formatted, by = "gene", all = TRUE)
}
}
rownames(full_results_genes) <- full_results_genes$gene
full_results_genes <- subset(full_results_genes, select = -c(gene))
for (row in 1:nrow(full_results_genes)){
num_pairs_sig_regulated = 0
num_pairs_sig_downregulated = 0
num_pairs_sig_upregulated = 0
for (pair in sensitive_resistant_pairs) {
padj = full_results_genes[row, str_interp("${pair}_padj")]
l2fc = full_results_genes[row, str_interp("${pair}_l2fc")]
if (!is.na(padj) && padj < 0.05) {
if (l2fc < 0) {
num_pairs_sig_downregulated = num_pairs_sig_downregulated + 1
} else if (l2fc > 0) {
num_pairs_sig_upregulated = num_pairs_sig_upregulated + 1
}
}
}
full_results_genes[row, "num_pairs_sig_up_regulated"] = num_pairs_sig_upregulated
full_results_genes[row, "num_pairs_sig_down_regulated"] = num_pairs_sig_downregulated
full_results_genes[row, "num_pairs_sig_regulated"] = num_pairs_sig_upregulated + num_pairs_sig_downregulated
}
full_results_genes = full_results_genes %>%
relocate(num_pairs_sig_down_regulated)%>%
relocate(num_pairs_sig_up_regulated) %>%
relocate(num_pairs_sig_regulated)
full_results_genes = full_results_genes[order(full_results_genes$num_pairs_sig_regulated, decreasing = TRUE),]
print(full_results_genes)
write.csv(full_results_genes, file = "deseq/output/differential_gene_expression_all_sensitive_resistant_pairs.csv")
Combine differential pathway data
### Combine results across pairs
full_results_pathways <- data.frame()
for (pair in sensitive_resistant_pairs) {
pair_up_pathways_file <- str_interp("deseq/output/${pair}_significantly_upregulated_pathways.csv")
pair_up_pathways <- as.data.frame(read.csv(pair_up_pathways_file, sep = ",", header = TRUE, row.names = 1))
pair_down_pathways_file <- str_interp("deseq/output/${pair}_significantly_downregulated_pathways.csv")
pair_down_pathways <- as.data.frame(read.csv(pair_down_pathways_file, sep = ",", header = TRUE, row.names = 1))
pair_pathways = rbind(pair_up_pathways, pair_down_pathways)
pair_pathways$ID = rownames(pair_pathways)
pair_pathways_formatted = pair_pathways[, c("ID", "Description", "p.adjust", "NES")]
colnames(pair_pathways_formatted) <- c("ID", "Description", str_interp("${pair}_padj"), str_interp("${pair}_NES"))
if (ncol(full_results_pathways) == 0) {
full_results_pathways = pair_pathways_formatted
} else {
full_results_pathways <- merge(full_results_pathways, pair_pathways_formatted, by = c("ID", "Description"), all = TRUE)
}
}
rownames(full_results_pathways) <- full_results_pathways$ID
full_results_pathways <- subset(full_results_pathways, select = -c(ID))
for (row in 1:nrow(full_results_pathways)){
num_pairs_sig_regulated = 0
num_pairs_sig_downregulated = 0
num_pairs_sig_upregulated = 0
for (pair in sensitive_resistant_pairs) {
padj = full_results_pathways[row, str_interp("${pair}_padj")]
NES = full_results_pathways[row, str_interp("${pair}_NES")]
if (!is.na(padj) && padj < 0.05) {
if (NES < 0) {
num_pairs_sig_downregulated = num_pairs_sig_downregulated + 1
} else if (NES > 0) {
num_pairs_sig_upregulated = num_pairs_sig_upregulated + 1
}
}
}
full_results_pathways[row, "num_pairs_sig_up_regulated"] = num_pairs_sig_upregulated
full_results_pathways[row, "num_pairs_sig_down_regulated"] = num_pairs_sig_downregulated
full_results_pathways[row, "num_pairs_sig_regulated"] = num_pairs_sig_upregulated + num_pairs_sig_downregulated
}
full_results_pathways = full_results_pathways %>%
relocate(num_pairs_sig_down_regulated)%>%
relocate(num_pairs_sig_up_regulated) %>%
relocate(num_pairs_sig_regulated)
full_results_pathways = full_results_pathways[order(full_results_pathways$num_pairs_sig_regulated, decreasing = TRUE),]
print(full_results_pathways)
write.csv(full_results_pathways, file = "deseq/output/differential_pathways_all_sensitive_resistant_pairs.csv")
Plot differential pathways
Use REVIGO to cluster pathways
similarity_matrix = calculateSimMatrix(rownames(full_results_pathways),
orgdb = "org.Hs.eg.db",
ont = "BP",
method = "Rel")
Warning in GOSemSim::godata(orgdb, ont = ont, keytype = keytype) :
use 'annoDb' instead of 'OrgDb'
preparing gene to GO mapping data...
preparing IC data...
Warning in calculateSimMatrix(rownames(full_results_pathways), orgdb = "org.Hs.eg.db", :
Removed 2 terms that were not found in orgdb for BP
scores = rep(1, ncol(similarity_matrix))
names(scores) = colnames(similarity_matrix)
reducedTerms = reduceSimMatrix(similarity_matrix,
scores,
threshold = 0.8,
orgdb = "org.Hs.eg.db")
'select()' returned 1:many mapping between keys and columns
Plot consistently differential pathways for all celllines in one
plot
darken_color <- function(color, factor = 0.5) {
# Convert color to RGB
rgb_vals <- col2rgb(color) / 255
# Darken each RGB channel
darkened_vals <- rgb_vals * factor
# Ensure values are within valid range [0, 1]
darkened_vals <- pmin(pmax(darkened_vals, 0), 1)
# Convert back to hexadecimal color
darkened_color <- rgb(darkened_vals[1], darkened_vals[2], darkened_vals[3], maxColorValue = 255)
return(darkened_color)
}
format_pathway_results = full_results_pathways
# Subset to terms able to be clustered
format_pathway_results = format_pathway_results[rownames(format_pathway_results) %in% rownames(reducedTerms),]
format_pathway_results = format_pathway_results[,- which(colnames(format_pathway_results) %in% c("num_pairs_sig_down_regulated", "num_pairs_sig_up_regulated"))]
# Change formatting of column names so that it is easier for pivot function below
colnames(format_pathway_results) = colnames(format_pathway_results) %>%
map_chr(\(x) sub("_(?=[^_]*$)", "-", x, perl = TRUE))
# Set parent terms
format_pathway_results$parentPathwayId = rownames(format_pathway_results) %>%
map_chr(\(x) reducedTerms[rownames(reducedTerms) == x, "parent"])
format_pathway_results$parentPathwayDescription = format_pathway_results$parentPathwayId %>%
map_chr(\(x) paste0(format_pathway_results[rownames(format_pathway_results) == x, "Description"], " (", nrow(format_pathway_results[format_pathway_results$parentPathwayId == x,]), ")"))
pivot_cols = colnames(format_pathway_results)
pivot_cols = pivot_cols[!(pivot_cols %in% c("Description", "num_pairs_sig-regulated", "parentPathwayId", "parentPathwayDescription"))]
plot_data = pivot_longer(data = format_pathway_results,
cols = pivot_cols,
names_to = c("celllinePair", ".value"),
names_sep = "-")
Warning: Using an external vector in selections was deprecated in tidyselect 1.1.0.
ℹ Please use `all_of()` or `any_of()` instead.
# Was:
data %>% select(pivot_cols)
# Now:
data %>% select(all_of(pivot_cols))
See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
# Score the term categories by combining scores across all child pathways and all cellline pairs
plot_data$score = plot_data$NES
# plot_data$score = -log10(plot_data$padj) * plot_data$NES
plot_data = plot_data %>%
group_by(parentPathwayId) %>%
mutate(category_score = mean(score, na.rm = TRUE))
# Order terms by the score of the parent pathway
plot_data = plot_data %>% arrange(category_score, score)
plot_data$parentPathwayDescription <- factor(plot_data$parentPathwayDescription, levels = unique(plot_data$parentPathwayDescription))
# Shift NES so that the -1 to 1 region doesn't go unused
if (any(abs(plot_data$NES)) < 1) {
print("ERROR: This plot will be messed up because we assumed novalues of NES between -1 and 1.")
}
Warning in any(abs(plot_data$NES)) :
coercing argument of type 'double' to logical
# plot_data$shift_NES = plot_data$NES %>%
# map_dbl(\(x) ifelse(x > 1, x - 1, ifelse(x < -1, x + 1, x)))
ggplot(plot_data, aes(x = NES, y = parentPathwayDescription, size = -log10(padj), color = celllinePair)) +
geom_point(position = position_jitter(height = 0.1), alpha = 0.5, layer = "above") + # Add jitter and reduce point transparency
scale_size_continuous(range = c(1, 5)) +
# Coloring by in vivo/in vitro resistance (OVCARs both in vitro; PEO and PEAs in vivo)
scale_color_manual(values = c("#F71300", "#E7CF00",
"#F55AFA", "#BD8161",
"#11DBCC", "#0E58E4",
"#14B70A")) +
geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), color = "gray", size = 0.1, layer = "below") +
geom_vline(xintercept = 0, linetype = "dashed", color = "black") +
labs(x = 'Normalized Enrichment Score', y = 'Pathway Category (# pathways)', color = 'Cellline Pair') +
ggtitle('Pathway regulation in resistant celllines (as compared to corresponding sensitive cellline)') +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
theme_classic()
Warning in geom_point(position = position_jitter(height = 0.1), alpha = 0.5, :
Ignoring unknown parameters: `layer`
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Warning in geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), :
Ignoring unknown parameters: `layer`
Warning: Removed 3191 rows containing missing values or values outside the scale range (`geom_point()`).

ggsave("deseq/output/differential_pathways_all_celllines_similarity_threshold_0.8.svg",
plot = last_plot(),
device = "svg",
width = 15,
height = 10,
units = "in")
Warning: Removed 3191 rows containing missing values or values outside the scale range (`geom_point()`).
## plot in vitro resistance only
ggplot(plot_data[plot_data$celllinePair %in% c("OVCAR4B_vs_OVCAR4", "OVCAR4A_vs_OVCAR4", "OVCAR3A_vs_OVCAR3", "OVCAR3B_vs_OVCAR3"),], aes(x = NES, y = parentPathwayDescription, size = -log10(padj), color = celllinePair)) +
geom_point(position = position_jitter(height = 0.1), alpha = 0.5, layer = "above") + # Add jitter and reduce point transparency
scale_size_continuous(range = c(1, 5)) +
scale_color_manual(values = c("#F71300", "#E7CF00",
"#F55AFA", "#BD8161")) +
geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), color = "gray", size = 0.1, layer = "below") +
geom_vline(xintercept = 0, linetype = "dashed", color = "black") +
labs(x = 'Normalized Enrichment Score', y = 'Pathway Category (# pathways)', color = 'Cellline Pair') +
ggtitle('Pathway regulation in in-vitro derived resistant celllines') +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
theme_classic()
Warning in geom_point(position = position_jitter(height = 0.1), alpha = 0.5, :
Ignoring unknown parameters: `layer`
Warning in geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), :
Ignoring unknown parameters: `layer`
Warning: Removed 1901 rows containing missing values or values outside the scale range (`geom_point()`).

## plot in vivo resistance only
ggplot(plot_data[plot_data$celllinePair %in% c("PEO4_vs_PEO2", "PEO6_vs_PEO2", "PEA2_vs_PEA1"),], aes(x = NES, y = parentPathwayDescription, size = -log10(padj), color = celllinePair)) +
geom_point(position = position_jitter(height = 0.1), alpha = 0.5, layer = "above") + # Add jitter and reduce point transparency
scale_size_continuous(range = c(1, 5)) +
scale_color_manual(values = c("#11DBCC", "#0E58E4",
"#14B70A")) +
geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), color = "gray", size = 0.1, layer = "below") +
geom_vline(xintercept = 0, linetype = "dashed", color = "black") +
labs(x = 'Normalized Enrichment Score', y = 'Pathway Category (# pathways)', color = 'Cellline Pair') +
ggtitle('Pathway regulation in in-vivo derived resistant celllines') +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
theme_classic()
Warning in geom_point(position = position_jitter(height = 0.1), alpha = 0.5, :
Ignoring unknown parameters: `layer`
Warning in geom_hline(yintercept = seq_along(levels(plot_data$parentPathwayDescription)), :
Ignoring unknown parameters: `layer`
Warning: Removed 439 rows containing missing values or values outside the scale range (`geom_point()`).

Plot differential pathways
Plot shows how the spread of significantly differential pathways
across sensitive/resistant pairs.
plot_data = full_results_pathways[,c("num_pairs_sig_up_regulated", "num_pairs_sig_down_regulated", "Description")]
print(plot_data)
# Orders primarily by how many more pairs were sig up than sig down regulated
# Orders secondarily by how few pairs were regulated in the non-dominant direction
# This ordering does the following: (7, 0) > (6, 0) > (5, 0) > (6, 1) > (7, 2)
plot_data <- plot_data %>%
mutate(order = ifelse(num_pairs_sig_up_regulated > num_pairs_sig_down_regulated,
num_pairs_sig_up_regulated - num_pairs_sig_down_regulated * 1.01,
-1 * (num_pairs_sig_down_regulated - num_pairs_sig_up_regulated * 1.01)))
plot_data <- plot_data %>%
arrange(desc(order))
plot_data = plot_data %>%
mutate(panel = ifelse(num_pairs_sig_up_regulated > num_pairs_sig_down_regulated,
"overall upregulated",
ifelse(num_pairs_sig_down_regulated == num_pairs_sig_up_regulated,
"evenly up and down regulated",
"overall downregulated")))
plot_data$panel = factor(plot_data$panel, levels = c("overall upregulated", "evenly up and down regulated", "overall downregulated"))
# organize the x coordinates based on panel
plot_data$left_placement = 0:(nrow(plot_data) - 1)
equal_panel_start = min(plot_data[plot_data$panel == "evenly up and down regulated", "left_placement"])
plot_data[plot_data$panel == "evenly up and down regulated",]$left_placement = plot_data[plot_data$panel == "evenly up and down regulated",]$left_placement - equal_panel_start
down_panel_start = min(plot_data[plot_data$panel == "overall downregulated", "left_placement"])
plot_data[plot_data$panel == "overall downregulated",]$left_placement = plot_data[plot_data$panel == "overall downregulated",]$left_placement - down_panel_start
plot_data$center_placement = plot_data$left_placement + 0.5
# Create bar plot
ggplot(plot_data) +
facet_wrap(~panel, ncol = 3) +
geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
scale_y_continuous(limits = c(-max(plot_data$num_pairs_sig_down_regulated) -1, max(plot_data$num_pairs_sig_up_regulated)) +1) +
theme_minimal() + # Apply a minimal theme
labs(x = "Pathways", y = "# resistant celllines showing significant regulation of pathway") + # Set axis labels
scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
theme_classic() +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
ggtitle('Pathway regulation in resistant celllines (as compared to corresponding sensitive cellline)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, :
Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, :
Ignoring unknown aesthetics: width

ggsave("deseq/output/consistently_differential_pathways_all_celllines.svg",
plot = last_plot(),
device = "svg",
width = 10,
height = 5,
units = "in")
Close up of consistently upregulated pathways
# End the plot between "categories" of pathways, including less than 100 pathways total
end_index = 1
for(i in 1:100) {
if (plot_data$order[i] != plot_data$order[i+1]) {
end_index = i
}
}
plot_data_up = plot_data[1:end_index,]
plot_data_up$pathway = plot_data_up$Description
print(plot_data_up)
ggplot(plot_data_up) +
geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
geom_text(aes(x = center_placement, y = 1, label = pathway), size = 2, color = "black", angle = 90) +
scale_y_continuous(limits = c(-max(plot_data_up$num_pairs_sig_down_regulated) -1, max(plot_data_up$num_pairs_sig_up_regulated)) +1) +
theme_minimal() + # Apply a minimal theme
labs(x = "Pathways", y = "# resistant celllines showing significant regulation of pathway") + # Set axis labels
scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
theme_classic() +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
ggtitle('Zooming in on left hand side (extremely consistently upregulated pathways)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, :
Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, :
Ignoring unknown aesthetics: width
ggsave("deseq/output/consistently_upregulated_pathways_all_celllines.svg",
plot = last_plot(),
device = "svg",
width = 10,
height = 5,
units = "in")

Close up of consistently downregulated pathways
# End the plot between "categories" of pathways, including less than 100 pathways total
start_index = 1
for(i in nrow(plot_data):(nrow(plot_data) - 99)) {
if (plot_data$order[i] != plot_data$order[i-1]) {
start_index = i
}
}
plot_data_down = plot_data[start_index:nrow(plot_data),]
plot_data_down$center_placement = plot_data_down$center_placement - start_index + 1 # Start at 0
plot_data_down$pathway = plot_data_down$Description
print(plot_data_down)
ggplot(plot_data_down) +
geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
geom_text(aes(x = center_placement, y = -1, label = pathway), size = 2, color = "black", angle = 90) +
scale_y_continuous(limits = c(-max(plot_data_down$num_pairs_sig_down_regulated) -1, max(plot_data_down$num_pairs_sig_up_regulated)) +1) +
theme_minimal() + # Apply a minimal theme
labs(x = "Pathways", y = "# resistant celllines showing significant regulation of pathway") + # Set axis labels
scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
theme_classic() +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
ggtitle('Zooming in on right hand side (extremely consistently downregulated pathways)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, :
Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, :
Ignoring unknown aesthetics: width
ggsave("deseq/output/consistently_downregulated_pathways_all_celllines.svg",
plot = last_plot(),
device = "svg",
width = 10,
height = 5,
units = "in")

Plot differential genes
Plot shows how the spread of significantly differential genes across
sensitive/resistant pairs.
# formatted_gene_results = full_results_genes[,c("num_pairs_sig_up_regulated", "num_pairs_sig_down_regulated")]
plot_data = full_results_genes[,c("num_pairs_sig_up_regulated", "num_pairs_sig_down_regulated")]
# plot_data <- full_results_genes %>%
# group_by(num_pairs_sig_up_regulated, num_pairs_sig_down_regulated) %>%
# summarise(count = n())
# Orders primarily by how many more pairs were sig up than sig down regulated
# Orders secondarily by how few pairs were regulated in the non-dominant direction
# This ordering does the following: (7, 0) > (6, 0) > (5, 0) > (6, 1) > (7, 2)
plot_data <- plot_data %>%
mutate(order = ifelse(num_pairs_sig_up_regulated > num_pairs_sig_down_regulated,
num_pairs_sig_up_regulated - num_pairs_sig_down_regulated * 1.01,
-1 * (num_pairs_sig_down_regulated - num_pairs_sig_up_regulated * 1.01)))
plot_data <- plot_data %>%
arrange(desc(order))
plot_data = plot_data %>%
mutate(panel = ifelse(num_pairs_sig_up_regulated > num_pairs_sig_down_regulated,
"overall upregulated",
ifelse(num_pairs_sig_down_regulated == num_pairs_sig_up_regulated,
"evenly up and down regulated",
"overall downregulated")))
plot_data$panel = factor(plot_data$panel, levels = c("overall upregulated", "evenly up and down regulated", "overall downregulated"))
# Add a small amount of color if not regulated
unregulated = plot_data$num_pairs_sig_up_regulated == 0 & plot_data$num_pairs_sig_down_regulated == 0
plot_data[unregulated,]$num_pairs_sig_up_regulated = 0.005
plot_data[unregulated,]$num_pairs_sig_down_regulated = -0.005
# organize the x coordinates based on panel
plot_data$left_placement = 0:(nrow(plot_data) - 1)
equal_panel_start = min(plot_data[plot_data$panel == "evenly up and down regulated", "left_placement"])
plot_data[plot_data$panel == "evenly up and down regulated",]$left_placement = plot_data[plot_data$panel == "evenly up and down regulated",]$left_placement - equal_panel_start
down_panel_start = min(plot_data[plot_data$panel == "overall downregulated", "left_placement"])
plot_data[plot_data$panel == "overall downregulated",]$left_placement = plot_data[plot_data$panel == "overall downregulated",]$left_placement - down_panel_start
plot_data$center_placement = plot_data$left_placement + 0.5
# next_placement = 0
# for(row in 1:nrow(plot_data)) {
# new_placement = next_placement
# plot_data[row, "left_placement"] = new_placement
# next_placement = new_placement + plot_data[row, "count"]
# }
# Need the center of the bar for plotting purposes
# plot_data$center_placement = plot_data$left_placement + plot_data$count/2
print(plot_data)
# Create bar plot
ggplot(plot_data) +
facet_wrap(~panel, ncol = 3, scales = "fixed") +
geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
# geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = count), stat = "identity") +
# geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = count), stat = "identity") +
scale_y_continuous(limits = c(-max(plot_data$num_pairs_sig_down_regulated) -1, max(plot_data$num_pairs_sig_up_regulated)) +1) +
# Set y-axis limits
# coord_flip() + # Flip the coordinates to create a sideways plot
theme_minimal() + # Apply a minimal theme
labs(x = "Genes", y = "# resistant celllines showing significant regulation of gene") + # Set axis labels
scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
theme_classic() +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
# axis.ticks.x = element_blank(),
# axis.text.x = element_blank()) +
ggtitle('Gene regulation in resistant celllines (as compared to corresponding sensitive cellline)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, :
Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, :
Ignoring unknown aesthetics: width

ggsave("deseq/output/differential_genes_all_celllines.svg",
plot = last_plot(),
device = "svg",
width = 10,
height = 5,
units = "in")
Close up of consistently upregulated genes
# End the plot between "categories" of genes, including less than 100 genes total
end_index = 1
for(i in 1:100) {
if (plot_data$order[i] != plot_data$order[i+1]) {
end_index = i
}
}
plot_data_up = plot_data[1:end_index,]
plot_data_up$gene = rownames(plot_data_up)
print(plot_data_up)
ggplot(plot_data_up) +
geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
geom_text(aes(x = center_placement, y = 1, label = gene), size = 2, color = "black", angle = 90) +
# geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = count), stat = "identity") +
# geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = count), stat = "identity") +
scale_y_continuous(limits = c(-max(plot_data_up$num_pairs_sig_down_regulated) -1, max(plot_data_up$num_pairs_sig_up_regulated)) +1) +
# Set y-axis limits
# coord_flip() + # Flip the coordinates to create a sideways plot
theme_minimal() + # Apply a minimal theme
labs(x = "Genes", y = "# resistant celllines showing significant regulation of gene") + # Set axis labels
scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
theme_classic() +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
# axis.ticks.x = element_blank(),
# axis.text.x = element_blank()) +
ggtitle('Zooming in on left hand side (extremely consistently upregulated genes)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, :
Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, :
Ignoring unknown aesthetics: width
# ggtitle('Genes extremely consistently upregulated in resistant celllines')
ggsave("deseq/output/consistently_upregulated_genes_all_celllines.svg",
plot = last_plot(),
device = "svg",
width = 10,
height = 5,
units = "in")

Close up of consistently downregulated genes
# End the plot between "categories" of genes, including less than 100 genes total
start_index = 1
for(i in nrow(plot_data):(nrow(plot_data) - 99)) {
if (plot_data$order[i] != plot_data$order[i-1]) {
start_index = i
}
}
plot_data_down = plot_data[start_index:nrow(plot_data),]
plot_data_down$center_placement = plot_data_down$center_placement - start_index + 1 # Start at 0
plot_data_down$gene = rownames(plot_data_down)
print(plot_data_down)
ggplot(plot_data_down) +
geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = 1), stat = "identity") +
geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = 1), stat = "identity") +
geom_text(aes(x = center_placement, y = 2, label = gene), size = 2, color = "black", angle = 90) +
# geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, fill = "downregulation", width = count), stat = "identity") +
# geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, fill = "upregulation", width = count), stat = "identity") +
scale_y_continuous(limits = c(-max(plot_data_down$num_pairs_sig_down_regulated) -1, max(plot_data_down$num_pairs_sig_up_regulated)) +1) +
# Set y-axis limits
# coord_flip() + # Flip the coordinates to create a sideways plot
theme_minimal() + # Apply a minimal theme
labs(x = "Genes", y = "# resistant celllines showing significant regulation of gene") + # Set axis labels
scale_fill_manual(values = c("upregulation" = "green", "downregulation" = "red")) + # Set fill colors
theme_classic() +
theme(panel.grid.minor.x = element_blank(),
panel.grid.major.x = element_blank(),
panel.grid.minor.y = element_blank(),
panel.grid.major.y = element_blank()) +
# axis.ticks.x = element_blank(),
# axis.text.x = element_blank()) +
ggtitle('Zooming in on right hand side (extremely consistently downregulated genes)')
Warning in geom_bar(aes(x = center_placement, y = -1 * num_pairs_sig_down_regulated, :
Ignoring unknown aesthetics: width
Warning in geom_bar(aes(x = center_placement, y = num_pairs_sig_up_regulated, :
Ignoring unknown aesthetics: width
# ggtitle('Genes consistently upregulated in resistant celllines')
ggsave("deseq/output/consistently_downregulated_genes_all_celllines.svg",
plot = last_plot(),
device = "svg",
width = 10,
height = 5,
units = "in")

Upset plots
Upset plots are akin to a Venn diagram and show the overlap of genes
that were significantly up and down regulated by each resistant cellline
(as compared to its respective control).
Note that these upset plots use the “distinct” mode (meaning that
each represented intersection consists of the “intersection elements
that belong to the sets defining the intersection but not to any other
set”).
TODO: which of the hundreds of possible combinations to show? For now
I’m separating upregulation and downregulation to decrease the number of
possible intersections by a lot, but I’m still only showing the top 50
intersections (ranked by degree – i.e. number of cellLines that share
that up or downregulated gene).
Combined up and down gene upset plot
combinedUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
for (pair in sensitive_resistant_pairs) {
split <- strsplit(pair, "_vs_")
cont <- split[[1]][2]
exp <- split[[1]][1]
for (dir in c("up", "down")) {
regulatedGenesFile = str_interp("deseq/output/${exp}_vs_${cont}_significantly_${dir}regulated_genes.csv")
regulatedGenes <- as.data.frame(read.csv(regulatedGenesFile, sep = ",", header = TRUE, row.names = 1))
key = str_interp("${exp}_sig_${dir}reg_genes")
combinedUpsetListInput[[key]] = rownames(regulatedGenes)
}
}
upset(fromList(combinedUpsetListInput), nsets = 14, nintersects = 50, order.by = "degree")


Upregulated gene upset plot
Shows only the upregulated genes.
upregUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
for (pair in sensitive_resistant_pairs) {
split <- strsplit(pair, "_vs_")
cont <- split[[1]][2]
exp <- split[[1]][1]
upGenesFile <- str_interp("deseq/output/${exp}_vs_${cont}_significantly_upregulated_genes.csv")
upGenes <- as.data.frame(read.csv(upGenesFile, sep = ",", header = TRUE, row.names = 1))
key = str_interp("${exp}_sig_upreg_genes")
upregUpsetListInput[[key]] = rownames(upGenes)
}
upset(fromList(upregUpsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


Output the lists of genes corresponding to each column of the upset
plot
fromList(upregUpsetListInput)
upregUpsetPlotGenes.df <- getUpsetPlotData(upregUpsetListInput)
write.csv(upregUpsetPlotGenes.df, file = "deseq/output/upset_plot_lists_significantly_upregulated_genes.csv")
upregUpsetPlotGenes.df
Downregulated gene upset plot
Shows only the downregulated genes.
downregUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
first_pair = TRUE
consistent_down_genes = c()
for (pair in sensitive_resistant_pairs) {
split <- strsplit(pair, "_vs_")
exp <- split[[1]][1]
downGenesFile <- str_interp("deseq/output/${pair}_significantly_downregulated_genes.csv")
downGenes <- as.data.frame(read.csv(downGenesFile, sep = ",", header = TRUE, row.names = 1))
key = str_interp("${exp}_sig_downreg_genes")
downregUpsetListInput[[key]] = rownames(downGenes)
if (first_pair == TRUE) {
consistent_down_genes = rownames(downGenes)
first_pair = FALSE
} else {
consistent_down_genes = intersect(consistent_down_genes, rownames(downGenes))
}
}
upset(fromList(downregUpsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


print("Genes downregulated in all sensitive/resistant pairs")
[1] "Genes downregulated in all sensitive/resistant pairs"
print(consistent_down_genes)
[1] "NREP" "TOX" "NR2F1" "LINC00960"
Output the lists of genes corresponding to each column of the upset
plot
downregUpsetPlotGenes.df <- getUpsetPlotData(downregUpsetListInput)
write.csv(downregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_downregulated_genes.csv")
downregUpsetPlotGenes.df
Upset plots across isolines
Gene included in isoline if it is sig upregulated in ANY of the cell
lines
upsetListInput <- vector(mode="list", length=length(isolines))
for (isoline in unique(isolines$isoline)) {
pairs = isolines[isolines$isoline == isoline, "pair"]
upGenes <- list()
for (pair in pairs) {
upGenesFile = str_interp("deseq/output/${pair}_significantly_upregulated_genes.csv")
newUpGenes <- as.data.frame(read.csv(upGenesFile, sep = ",", header = TRUE, row.names = 1))
upGenes <- unique(c(upGenes, rownames(newUpGenes)))
}
upsetListInput[[isoline]] = upGenes
}
upset(fromList(upsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


# Output the lists of genes corresponding to each column of the upset plot
upregUpsetPlotGenes.df <- getUpsetPlotData(upsetListInput)
write.csv(upregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_upregulated_genes_any_sublines.csv")
upregUpsetPlotGenes.df
Gene included in isoline if it is sig upregulated in ALL of the cell
lines
upsetListInput <- vector(mode="list", length=length(isolines))
for (isoline in unique(isolines$isoline)) {
pairs = isolines[isolines$isoline == isoline, "pair"]
pair = pairs[1]
upGenesFile = str_interp("deseq/output/${pair}_significantly_upregulated_genes.csv")
upGenes <- as.data.frame(read.csv(upGenesFile, sep = ",", header = TRUE, row.names = 1))
upGenes <- rownames(upGenes)
if (length(pairs) > 1) {
for (pair in pairs[2:length(pairs)]) {
upGenesFile = str_interp("deseq/output/${pair}_significantly_upregulated_genes.csv")
newUpGenes <- as.data.frame(read.csv(upGenesFile, sep = ",", header = TRUE, row.names = 1))
upGenes <- intersect(upGenes, rownames(newUpGenes))
}
}
upsetListInput[[isoline]] = upGenes
}
upset(fromList(upsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


# Output the lists of genes corresponding to each column of the upset plot
upregUpsetPlotGenes.df <- getUpsetPlotData(upsetListInput)
write.csv(upregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_upregulated_genes_all_sublines.csv")
upregUpsetPlotGenes.df
Gene included in isoline if it is sig downregulated in ANY of the
cell lines
upsetListInput <- vector(mode="list", length=length(isolines))
for (isoline in unique(isolines$isoline)) {
pairs = isolines[isolines$isoline == isoline, "pair"]
downGenes <- list()
for (pair in pairs) {
downGenesFile = str_interp("deseq/output/${pair}_significantly_downregulated_genes.csv")
newDownGenes <- as.data.frame(read.csv(downGenesFile, sep = ",", header = TRUE, row.names = 1))
downGenes <- unique(c(downGenes, rownames(newDownGenes)))
}
upsetListInput[[isoline]] = downGenes
}
upset(fromList(upsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


# Output the lists of genes corresponding to each column of the upset plot
downregUpsetPlotGenes.df <- getUpsetPlotData(upsetListInput)
write.csv(downregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_downregulated_genes_any_sublines.csv")
downregUpsetPlotGenes.df
Gene included in isoline if it is sig downregulated in ALL of the
cell lines
upsetListInput <- vector(mode="list", length=length(isolines))
for (isoline in unique(isolines$isoline)) {
pairs = isolines[isolines$isoline == isoline, "pair"]
pair = pairs[1]
downGenesFile = str_interp("deseq/output/${pair}_significantly_downregulated_genes.csv")
downGenes <- as.data.frame(read.csv(downGenesFile, sep = ",", header = TRUE, row.names = 1))
downGenes <- rownames(downGenes)
if (length(pairs) > 1) {
for (pair in pairs[2:length(pairs)]) {
downGenesFile = str_interp("deseq/output/${pair}_significantly_downregulated_genes.csv")
newDownGenes <- as.data.frame(read.csv(downGenesFile, sep = ",", header = TRUE, row.names = 1))
downGenes <- intersect(downGenes, rownames(newDownGenes))
}
}
upsetListInput[[isoline]] = downGenes
}
upset(fromList(upsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


# Output the lists of genes corresponding to each column of the upset plot
downregUpsetPlotGenes.df <- getUpsetPlotData(upsetListInput)
write.csv(downregUpsetPlotGenes.df, file = "deseq/output/upset_plot_isolines_lists_significantly_downregulated_genes_all_sublines.csv")
upregUpsetPlotGenes.df
Upregulated pathway upset plot
Shows only the upregulated pathways.
upregUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
for (pair in sensitive_resistant_pairs) {
split <- strsplit(pair, "_vs_")
cont <- split[[1]][2]
exp <- split[[1]][1]
upPathwaysFile <- str_interp("deseq/output/${exp}_vs_${cont}_significantly_upregulated_pathways.csv")
upPathways <- as.data.frame(read.csv(upPathwaysFile, sep = ",", header = TRUE, row.names = 1))
key = str_interp("${exp}_sig_upreg_pathways")
upregUpsetListInput[[key]] = upPathways$Description
}
upset(fromList(upregUpsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


Output the lists of pathways corresponding to each column of the
upset plot
fromList(upregUpsetListInput)
upregUpsetPlotPathways.df <- getUpsetPlotData(upregUpsetListInput)
write.csv(upregUpsetPlotPathways.df, file = "deseq/output/upset_plot_lists_significantly_upregulated_pathways.csv")
upregUpsetPlotPathways.df
Downregulated pathway upset plot
Shows only the downregulated pathways.
downregUpsetListInput <- vector(mode="list", length=length(sensitive_resistant_pairs))
for (pair in sensitive_resistant_pairs) {
split <- strsplit(pair, "_vs_")
cont <- split[[1]][2]
exp <- split[[1]][1]
downPathwaysFile <- str_interp("deseq/output/${exp}_vs_${cont}_significantly_downregulated_pathways.csv")
downPathways <- as.data.frame(read.csv(downPathwaysFile, sep = ",", header = TRUE, row.names = 1))
key = str_interp("${exp}_sig_downreg_pathways")
downregUpsetListInput[[key]] = downPathways$Description
}
upset(fromList(downregUpsetListInput), nsets = 7, nintersects = 50, order.by = "degree")


Output the lists of pathways corresponding to each column of the
upset plot
fromList(downregUpsetListInput)
downregUpsetPlotPathways.df <- getUpsetPlotData(downregUpsetListInput)
write.csv(downregUpsetPlotPathways.df, file = "deseq/output/upset_plot_lists_significantly_downregulated_pathways.csv")
downregUpsetPlotPathways.df
Read in platinum mechanism data
platinumGeneData <- as.data.frame(read.table("deseq/specific-pathways/platinum-list-all-mechanisms.txt", sep = "\n", header = TRUE))
row.names(platinumGeneData) <- platinumGeneData[,1]
platinumGeneData
LS0tCnRpdGxlOiAiU2Vjb25kYXJ5IGFuYWx5c2lzIG9mIDIzMDMyNC0zd2F5LW1lcmdlIFJOQS1zZXEgc2Vuc2l0aXZlIHZzIHJlc2lzdGFudCBjZWxsIGxpbmVzIGluIGlzb2xpbmVzIFBFTywgUEVBLCBPVkNBUjMsIE9WQ0FSNCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgSW50cm9kdWN0aW9uICAKCkdvYWw6IHRvIGNvbXBhcmUgdGhlIGRpZmZlcmVuY2UgaW4gZ2VuZSBleHByZXNzaW9uIGJldHdlZW4gc2Vuc2l0aXZlIGFuZCByZXNpc3RhbnQgY2VsbCBsaW5lcyBvZiB0aHJlZSBkaWZmZXJlbnQgbGluZXMgb2YgdHVtb3JzLgoKUk5BLXNlcSB3YXMgcnVuIGZvciB0aHJlZSBpc29nZW5pYyB0dW1vciBjZWxsIGxpbmVzIChQRU8xLCBQRU80LCBhbmQgUEVPNikKU2FtcGxlIHByZXBhcmF0aW9uIHdhcyBwZXJmb3JtZWQgaW4gRHIuIExhbmcncyBsYWIuIFByZXBhcmF0aW9uIG9mIGNlbGxzIGFuZCBSTkEgZXh0cmFjdGlvbiB3YXMgZG9uZSBieSBLZW5kcmEsIEpvc2llLCBhbmQgU3lkbmV5LgpSTkEgc2VxIGxpYnJhcnkgcHJlcCB3YXMgZG9uZSBieSBLcmlzdGVuLgpBbmFseXNpcyBkb25lIGJ5IFJ5YW4uCgpCaW9sb2dpY2FsIFF1ZXN0aW9uczoKCiAqIEhvdyBkb2VzIHRoZSBnZW5lIGV4cHJlc3Npb24gZGlmZmVyIGJldHdlZW4gdGhlc2UgdGhyZWUgY2VsbCBsaW5lcz8KICogSG93IGRvZXMgdGhlIGdlbmUgZXhwcmVzc2lvbiBkaWZmZXIgYmV0d2VlbiBzZW5zaXRpdmUgYW5kIHJlc2lzdGFudCBjZWxsIGxpbmVzCgojIyBJbnB1dHMKCklucHV0cyBjb25zaXN0ZWQgb2Y6ICAKCiAqIE1ldGFkYXRhIHNwcmVhZHNoZWV0IGZvciBERVNlcTIKICogc2FsbW9uLm1lcmdlZC5nZW5lX2NvdW50cy50c3YgZmlsZSBmcm9tIG5mLWNvcmUvcm5hc2VxIHBpcGVsaW5lIG91dHB1dCAgCiAqIHNhbG1vbi5tZXJnZWQuZ2VuZV90cG0udHN2CgpgYGB7ciBsb2FkIHBhY2thZ2VzLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KHZzbikKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShiaW9tYVJ0KQpsaWJyYXJ5KERFU2VxQW5hbHlzaXMpCmxpYnJhcnkoVXBTZXRSKQpsaWJyYXJ5KGdwcm9maWxlcjIpCmxpYnJhcnkocnJ2Z28pCmxpYnJhcnkoR08uZGIpCmxpYnJhcnkoZ2dmb3J0aWZ5KQpHTyA8LSBhcy5saXN0KEdPVEVSTSkKYGBgCgpEZWZpbmUgZnVuY3Rpb25zIHRvIG1ha2UgYW4gaW50ZXJhY3RpdmUgcGxvdHMgZnJvbSByZXZpZ28gZGF0YSAoY29kZSBiYXNlZCBvZmYgYHNoaW55X3JydmdvYCkKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CnJldmlnb19zY2F0dGVycGxvdCA8LSBmdW5jdGlvbihzaW1NYXRyaXgsIHJlZHVjZWRUZXJtcykgewogIHggPC0gY21kc2NhbGUoYXMubWF0cml4KGFzLmRpc3QoMS1zaW1NYXRyaXgpKSwgZWlnPVRSVUUsIGs9MikKICBkZiA8LSBjYmluZChhcy5kYXRhLmZyYW1lKHgkcG9pbnRzKSwKICAgICAgICAgICAgICByZWR1Y2VkVGVybXNbbWF0Y2gocm93bmFtZXMoeCRwb2ludHMpLCByZWR1Y2VkVGVybXMkZ28pLCBjKCJ0ZXJtIiwgInBhcmVudCIsICJwYXJlbnRUZXJtIiwgInNpemUiKV0pCiAgCiAgcCA8LSAgc2NhdHRlclBsb3Qoc2ltTWF0cml4LCByZWR1Y2VkVGVybXMsIGFkZExhYmVsPUZBTFNFKQogIAogIHAgPC0gcCArIGdlb21fdGV4dChhZXMobGFiZWw9cGFyZW50VGVybSksIGRhdGE9c3Vic2V0KGRmLCBwYXJlbnQgPT0gcm93bmFtZXMoZGYpKSwgc2l6ZT0zKSAjIHNjYXR0ZXIgbGFiZWxzCiAgcCA8LSBwICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdub25lJykgIyBzY2F0dGVyIGxlZ2VuZCAKICBnZ3Bsb3RseShwKQp9CgpyZXZpZ29faGVhdG1hcCA8LSBmdW5jdGlvbihzaW1NYXRyaXgsIHJlZHVjZWRUZXJtcykgewogIGFubiA8LSByZWR1Y2VkVGVybXMkdGVybVttYXRjaChyZWR1Y2VkVGVybXMkcGFyZW50LCByZWR1Y2VkVGVybXMkZ28pXQogIGFubiA8LSBkYXRhLmZyYW1lKGFublttYXRjaChyb3duYW1lcyhzaW1NYXRyaXgpLCByZWR1Y2VkVGVybXMkZ28pXSkKICBjb2xuYW1lcyhhbm4pIDwtICIiCiAgaGVhdG1hcGx5OjpoZWF0bWFwbHkoc2ltTWF0cml4LCByb3dfc2lkZV9jb2xvcnM9YW5uLCBwbG90X21ldGhvZD0icGxvdGx5IiwKICAgICAgICAgICAgICAgICAgICAgICBzeW1tPVRSVUUsIGxhYlJvdz1OVUxMLCBrZXkudGl0bGU9IlNpbWlsYXJpdHkiLAogICAgICAgICAgICAgICAgICAgICAgIHNob3d0aWNrbGFiZWxzPWMoRkFMU0UsIFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgIHdpZHRoPTEwMjQsIGhlaWdodD0xMDI0LAogICAgICAgICAgICAgICAgICAgICAgIHJvd19zaWRlX3BhbGV0dGU9cnJ2Z286OjpnZ19jb2xvcl9odWUsCiAgICAgICAgICAgICAgICAgICAgICAgc2hvd19kZW5kcm9ncmFtPXJlcChUUlVFLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICBmb250c2l6ZV9yb3c9NikgJT4lCiAgY29sb3JiYXIoeGFuY2hvcj0ibGVmdCIsIHlhbmNob3I9ImJvdHRvbSIsIGxlbj0uMiwgdGlja2ZvbnQ9bGlzdChzaXplPTYpLCB3aGljaD0xKSAlPiUKICBjb2xvcmJhcih4YW5jaG9yPSJsZWZ0IiwgeWFuY2hvcj0iYm90dG9tIiwgbGVuPS41LCB0aWNrZm9udD1saXN0KHNpemU9NiksIHdoaWNoPTIpICU+JQogIGxheW91dCh3aWR0aD0xMDAwKQp9CmBgYAoKRGVmaW5lIHVwc2V0IHBsb3QgaGVscGVyIGZ1bmN0aW9ucyAoY29kZSBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS9obXMtZGJtaS9VcFNldFIvaXNzdWVzLzg1I2lzc3VlY29tbWVudC00MTU0ODA5NTQpCmBgYHtyIGluY2x1ZGU9RkFMU0V9Cm92ZXJsYXBHcm91cHMgPC0gZnVuY3Rpb24gKGxpc3RJbnB1dCwgc29ydCA9IFRSVUUpIHsKICAjIGxpc3RJbnB1dCBjb3VsZCBsb29rIGxpa2UgdGhpczoKICAjICRvbmUKICAjIFsxXSAiYSIgImIiICJjIiAiZSIgImciICJoIiAiayIgImwiICJtIgogICMgJHR3bwogICMgWzFdICJhIiAiYiIgImQiICJlIiAiaiIKICAjICR0aHJlZQogICMgWzFdICJhIiAiZSIgImYiICJnIiAiaCIgImkiICJqIiAibCIgIm0iCiAgbGlzdElucHV0bWF0ICAgIDwtIGZyb21MaXN0KGxpc3RJbnB1dCkgPT0gMQogICMgICAgIG9uZSAgIHR3byB0aHJlZQogICMgYSAgVFJVRSAgVFJVRSAgVFJVRQogICMgYiAgVFJVRSAgVFJVRSBGQUxTRQogICMuLi4KICAjIGNvbmRlbnNpbmcgbWF0cml4IHRvIHVuaXF1ZSBjb21iaW5hdGlvbnMgZWxlbWVudHMKICBsaXN0SW5wdXR1bmlxdWUgPC0gdW5pcXVlKGxpc3RJbnB1dG1hdCkKICBncm91cGxpc3QgPC0gbGlzdCgpCiAgIyBnb2luZyB0aHJvdWdoIGFsbCB1bmlxdWUgY29tYmluYXRpb25zIGFuZCBjb2xsZWN0IGVsZW1lbnRzIGZvciBlYWNoIGluIGEgbGlzdAogIGZvciAoaSBpbiAxOm5yb3cobGlzdElucHV0dW5pcXVlKSkgewogICAgY3VycmVudFJvdyA8LSBsaXN0SW5wdXR1bmlxdWVbaSxdCiAgICBteWVsZW1lbnRzIDwtIHdoaWNoKGFwcGx5KGxpc3RJbnB1dG1hdCwxLGZ1bmN0aW9uKHgpIGFsbCh4ID09IGN1cnJlbnRSb3cpKSkKICAgIGF0dHIobXllbGVtZW50cywgImdyb3VwcyIpIDwtIGN1cnJlbnRSb3cKICAgIGdyb3VwbGlzdFtbcGFzdGUoY29sbmFtZXMobGlzdElucHV0dW5pcXVlKVtjdXJyZW50Um93XSwgY29sbGFwc2UgPSAiOiIpXV0gPC0gbXllbGVtZW50cwogICAgbXllbGVtZW50cwogICAgIyBhdHRyKCwiZ3JvdXBzIikKICAgICMgICBvbmUgICB0d28gdGhyZWUgCiAgICAjIEZBTFNFIEZBTFNFICBUUlVFIAogICAgIyAgZiAgaSAKICAgICMgMTIgMTMgCiAgfQogIGlmIChzb3J0KSB7CiAgICBncm91cGxpc3QgPC0gZ3JvdXBsaXN0W29yZGVyKHNhcHBseShncm91cGxpc3QsIGZ1bmN0aW9uKHgpIGxlbmd0aCh4KSksIGRlY3JlYXNpbmcgPSBUUlVFKV0KICB9CiAgYXR0cihncm91cGxpc3QsICJlbGVtZW50cyIpIDwtIHVuaXF1ZSh1bmxpc3QobGlzdElucHV0KSkKICByZXR1cm4oZ3JvdXBsaXN0KQp9CgpnZXRVcHNldFBsb3REYXRhIDwtIGZ1bmN0aW9uKGxpc3RJbnB1dCkgewogICMgbGlzdElucHV0IGNvdWxkIGxvb2sgbGlrZSB0aGlzOgogICMgJG9uZQogICMgWzFdICJhIiAiYiIgImMiICJlIiAiZyIgImgiICJrIiAibCIgIm0iCiAgIyAkdHdvCiAgIyBbMV0gImEiICJiIiAiZCIgImUiICJqIgogICMgJHRocmVlCiAgIyBbMV0gImEiICJlIiAiZiIgImciICJoIiAiaSIgImoiICJsIiAibSIKICB1cHNldFBsb3REYXRhLm1lc3N5IDwtIG92ZXJsYXBHcm91cHMobGlzdElucHV0KQogIHVwc2V0UGxvdERhdGEgPC0gcHVycnI6Om1hcCh1cHNldFBsb3REYXRhLm1lc3N5LCB+IGF0dHIodXBzZXRQbG90RGF0YS5tZXNzeSwgImVsZW1lbnRzIilbLnhdKQogICMgQWRkcyBOQSB2YWx1ZXMgdW50aWwgZWFjaCBpcyB0aGUgc2FtZSBsZW5ndGggc28gd2UgY2FuIGZvcm1hdCBpbiBhIGRhdGEgZnJhbWUKICB1cHNldFBsb3REYXRhLmRmIDwtIGFzLmRhdGEuZnJhbWUobGFwcGx5KHVwc2V0UGxvdERhdGEsIGBsZW5ndGg8LWAsIG1heChsZW5ndGhzKHVwc2V0UGxvdERhdGEpKSkpCiAgcmV0dXJuKHVwc2V0UGxvdERhdGEuZGYpCn0KYGBgCgojIyBERVNlcTIgLSBzZXR1cAoKIyMjIFJlYWQgaW4gbWV0YWRhdGEgdGFibGUKCmBgYHtyfQpzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzIDwtIGMoIk9WQ0FSM0FfdnNfT1ZDQVIzIiwgIk9WQ0FSM0JfdnNfT1ZDQVIzIiwgIk9WQ0FSNEFfdnNfT1ZDQVI0IiwgIk9WQ0FSNEJfdnNfT1ZDQVI0IiwgIlBFQTJfdnNfUEVBMSIsICJQRU82X3ZzX1BFTzEiLCAiUEVPNF92c19QRU8xIikKCmlzb2xpbmVzIDwtIGRhdGEuZnJhbWUocGFpciA9IHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMsCiAgICAgICAgICAgICAgICAgICAgICAgaXNvbGluZSA9IGMoIk9WQ0FSMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9WQ0FSNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFQSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFTyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBFTyIpKQpgYGAKCmBgYHtyIGxvYWQgbWV0YXRhYmxlfQptZXRhZGF0YS5hbGwgPC0gYXMuZGF0YS5mcmFtZShyZWFkLnRhYmxlKCJkZXNlcS9tZXRhZGF0YS5jc3YiLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUpKQpyb3duYW1lcyhtZXRhZGF0YS5hbGwpIDwtIG1ldGFkYXRhLmFsbCRTaG9ydE5hbWUKCiMgU2hvdWxkIHB1dCB0aGlzIGluIHRoZSBtZXRhZGF0YSBmaWxlLCBidXQganVzdCBkb2luZyB0aGlzIHRvIHNhdmUgdGltZQpmb3IgKHJvdyBpbiAxOm5yb3cobWV0YWRhdGEuYWxsKSkgewogICAgaXNvZ2VuaWNSYW5rIDwtIDEKICAgIHJlc2lzdGFudCA8LSAwCiAgICBpZiAobWV0YWRhdGEuYWxsJENlbGxMaW5lW3Jvd10gJWluJSBsaXN0KCJPVkNBUjNBIiwgIk9WQ0FSNEEiLCAiUEVBMiIsICJQRU80IikpIHsKICAgICAgaXNvZ2VuaWNSYW5rIDwtIDIKICAgICAgcmVzaXN0YW50IDwtIDEKICAgIH0gZWxzZSBpZiAobWV0YWRhdGEuYWxsJENlbGxMaW5lW3Jvd10gJWluJSBsaXN0KCJPVkNBUjNCIiwgIk9WQ0FSNEIiLCAiUEVPNiIpKSB7CiAgICAgIGlzb2dlbmljUmFuayA8LSAzCiAgICAgIHJlc2lzdGFudCA8LSAxCiAgICB9CiAgICBtZXRhZGF0YS5hbGwkSXNvZ2VuaWNSYW5rW3Jvd10gPC0gaXNvZ2VuaWNSYW5rCiAgICBtZXRhZGF0YS5hbGwkUmVzaXN0YW50W3Jvd10gPC0gcmVzaXN0YW50Cn0KbWV0YWRhdGEuYWxsCmBgYAoKIyMjIExvYWQgY291bnQgbWF0cml4CgpgYGB7ciByZWFkIGNvdW50IG1hdHJpeH0KY291bnRtYXRyaXggPC0gYXMubWF0cml4KHJlYWQudGFibGUoIi4uL3N0YXJfc2FsbW9uL3NhbG1vbi5tZXJnZWQuZ2VuZV9jb3VudHMudHN2Iiwgc2VwID0gIlx0IiwgaGVhZGVyID0gVFJVRSkpCnJvdy5uYW1lcyhjb3VudG1hdHJpeCkgPC0gY291bnRtYXRyaXhbLCAiZ2VuZV9uYW1lIl0KY291bnRtYXRyaXggPC0gY291bnRtYXRyaXhbLCAzOm5jb2woY291bnRtYXRyaXgpXQpjb3VudG1hdHJpeC5hbGwgPC0gbWF0cml4KGFzLm51bWVyaWMoY291bnRtYXRyaXgpLCBuY29sID0gbmNvbChjb3VudG1hdHJpeCksIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhjb3VudG1hdHJpeCksIGNvbG5hbWVzKGNvdW50bWF0cml4KSkpCmNvdW50bWF0cml4LmFsbCA8LSByb3VuZChjb3VudG1hdHJpeC5hbGwpCmNvdW50bWF0cml4LmFsbCA8LSBjb3VudG1hdHJpeC5hbGxbLCBtZXRhZGF0YS5hbGwkTG9uZ05hbWVdCmNvbG5hbWVzKGNvdW50bWF0cml4LmFsbCkgPC0gbWV0YWRhdGEuYWxsJFNob3J0TmFtZVttYXRjaChjb2xuYW1lcyhjb3VudG1hdHJpeC5hbGwpLCBtZXRhZGF0YS5hbGwkTG9uZ05hbWUpXSAjIFJlbmFtZXMgdGhlIFNhbG1vbiBjb3VudG1hdHJpeCB1c2luZyBmb3JtYXR0ZWQgc2hvcnQgbmFtZQphcy5kYXRhLmZyYW1lKGNvdW50bWF0cml4LmFsbCkKYGBgCgojIyMgTG9hZCBUUE0gbWF0cml4CmBgYHtyfQpUUE0gPC0gYXMubWF0cml4KHJlYWQuZGVsaW0oIi4uL3N0YXJfc2FsbW9uL3NhbG1vbi5tZXJnZWQuZ2VuZV90cG0udHN2Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz0iZ2VuZV9pZCIpKQpUUE0gPC0gVFBNWywtMV0KVFBNIDwtIG1hdHJpeChhcy5udW1lcmljKFRQTSksIG5jb2wgPSBuY29sKFRQTSksIGRpbW5hbWVzID0gbGlzdChyb3duYW1lcyhUUE0pLCBjb2xuYW1lcyhUUE0pKSkKVFBNLmxvZyA8LSBsb2coVFBNICsgMSkKYXMuZGF0YS5mcmFtZShUUE0ubG9nKQpjb2xuYW1lcyhUUE0ubG9nKSA8LSBtZXRhZGF0YS5hbGwkU2hvcnROYW1lCmBgYAoKIyMgUENBIHBsb3QKClJ1bm5pbmcgREVTZXEgb24gYWxsIG9mIHRoZSBjZWxsbGluZXMgdG9nZXRoZXIgdG8gZ2V0IG5vcm1hbGl6ZWQgZGF0YS4KCmBgYHtyfQpkZHMuYWxsID0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeCgKICBjb3VudERhdGEgPSBjb3VudG1hdHJpeC5hbGwsCiAgY29sRGF0YSA9IG1ldGFkYXRhLmFsbCwKICBkZXNpZ24gPSB+IFJlcGxpY2F0ZSArIENlbGxMaW5lCikKZGRzLmFsbCA9IERFU2VxKGRkcy5hbGwpCnNhdmUoZGRzLmFsbCwgZmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL1JkYXRhL2FsbF9jZWxsbGluZXNfZGRzLlJEYXRhIikpCiMgbG9hZChzdHJfaW50ZXJwKCJkZXNlcS9SZGF0YS9hbGxfY2VsbGxpbmVzX2Rkcy5SRGF0YSIpKQpgYGAKVHJhbnNmb3JtIGRhdGEgd2l0aCBhIHZhcmlhbmNlIHN0YWJpbGl6ZWQgdHJhbnNmb3JtYXRpb24KCmBgYHtyfQp2c2QuYWxsID0gYXNzYXkodnN0KGRkcy5hbGwpKQptZWFuU2RQbG90KHZzZC5hbGwpCmBgYAoKPCEtLSBQQ0EgUGxvdCAtLT4KCjwhLS0gT2xkIGZvcm1hdHRpbmcgLS0+Cgo8IS0tIGBgYHtyfSAtLT4KPCEtLSB2c2QuYWxsID0gYXMuZGF0YS5mcmFtZSh2c2QuYWxsKSAtLT4KPCEtLSB0cmFuc2Zvcm1lZCA9IGFzLmRhdGEuZnJhbWUodCh2c2QuYWxsKSkgLS0+CjwhLS0gcGNhUmVzID0gcHJjb21wKHRyYW5zZm9ybWVkWywtMV0sIHNjYWxlID0gVFJVRSkgLS0+Cgo8IS0tIHBjYV9tZXRhZGF0YSA9IGNvbERhdGEoZGRzLmFsbClbLGMoIkNlbGxMaW5lIiwgIlJlc2lzdGFudCIsICJJc29MaW5lIildIC0tPgo8IS0tIHBjYV9tZXRhZGF0YSRSZXNpc3RhbmNlIDwtIGlmZWxzZShwY2FfbWV0YWRhdGEkUmVzaXN0YW50ID09IDAsICJTZW5zaXRpdmUiLCAiUmVzaXN0YW50IikgLS0+CjwhLS0gcm93Lm5hbWVzKHBjYV9tZXRhZGF0YSkgPC0gY29sRGF0YShkZHMuYWxsKVssYygiU2hvcnROYW1lIildIC0tPgoKPCEtLSB0cmFuc2Zvcm1lZCRJc29MaW5lIDwtIGZhY3RvcihwY2FfbWV0YWRhdGEkSXNvTGluZSkgLS0+CjwhLS0gdHJhbnNmb3JtZWQkQ2VsbExpbmUgPC0gZmFjdG9yKHBjYV9tZXRhZGF0YSRDZWxsTGluZSkgLS0+CjwhLS0gdHJhbnNmb3JtZWQkUmVzaXN0YW5jZSA8LSBmYWN0b3IocGNhX21ldGFkYXRhJFJlc2lzdGFuY2UpIC0tPgoKPCEtLSBhdXRvcGxvdChwY2FSZXMsIGRhdGEgPSB0cmFuc2Zvcm1lZCwgY29sb3VyID0gIkNlbGxMaW5lIiwgc2hhcGUgPSAiUmVzaXN0YW5jZSIsIHNpemUgPSAzKSAtLT4KPCEtLSAjIGdnc2F2ZSgiZGVzZXEvb3V0cHV0L3BjYV9hbGxfY2VsbGxpbmVzLnN2ZyIsIC0tPgo8IS0tICMgICAgICAgIHBsb3QgPSBsYXN0X3Bsb3QoKSwgLS0+CjwhLS0gIyAgICAgICAgZGV2aWNlID0gInN2ZyIsIC0tPgo8IS0tICMgICAgICAgIHdpZHRoID0gNiwgLS0+CjwhLS0gIyAgICAgICAgdW5pdHMgPSAiaW4iKSAtLT4KCjwhLS0gIyBDb21wdXRlIHRvcCBjb250cmlidXRpbmcgZ2VuZXMgdG8gUEMxIGFuZCBQQzIgLS0+CjwhLS0gbG9hZGluZ3NfcGMxIDwtIHBjYVJlcyRyb3RhdGlvblssIlBDMSJdIC0tPgo8IS0tIGNvbnRyaWJ1dGluZ19nZW5lc19wYzEgPC0gbG9hZGluZ3NfcGMxW29yZGVyKGFicyhsb2FkaW5nc19wYzEpLCBkZWNyZWFzaW5nID0gVFJVRSldIC0tPgo8IS0tIHByaW50KGFzLmRhdGEuZnJhbWUoY29udHJpYnV0aW5nX2dlbmVzX3BjMSkpIC0tPgoKPCEtLSBsb2FkaW5nc19wYzIgPC0gcGNhUmVzJHJvdGF0aW9uWywgIlBDMiJdIC0tPgo8IS0tIGNvbnRyaWJ1dGluZ19nZW5lc19wYzIgPC0gbG9hZGluZ3NfcGMyW29yZGVyKGFicyhsb2FkaW5nc19wYzIpLCBkZWNyZWFzaW5nID0gVFJVRSldIC0tPgo8IS0tIHByaW50KGFzLmRhdGEuZnJhbWUoY29udHJpYnV0aW5nX2dlbmVzX3BjMikpIC0tPgoKPCEtLSBgYGAgLS0+CgpQQ0EgcGxvdCBmb3JtYXR0ZWQgZm9yIHBhcGVyCgpgYGB7cn0KdnNkLmFsbC5mdWxsb2JqID0gdnN0KGRkcy5hbGwpCnBjYURhdGEgPC0gcGxvdFBDQSh2c2QuYWxsLmZ1bGxvYmosIGludGdyb3VwPWMoIkNlbGxMaW5lIiwgIlJlc2lzdGFudCIpLCByZXR1cm5EYXRhPVRSVUUpCnBjYURhdGEkUmVzaXN0YW5jZSA8LSBpZmVsc2UocGNhRGF0YSRSZXNpc3RhbnQgPT0gMCwgIlNlbnNpdGl2ZSIsICJSZXNpc3RhbnQiKQpwZXJjZW50VmFyIDwtIHJvdW5kKDEwMCAqIGF0dHIocGNhRGF0YSwgInBlcmNlbnRWYXIiKSkKbXlDb2xvcnMgPC0gYygiIzc2QUI3RSIsICIjNjNFNjc4IiwgIiMwRTlEMUYiLCAiIzdCODdGRCIsICIjMUQzMkZCIiwgIiMxNDUyRkIiLCAiI0U4NzQyNiIsICIjREFDNDI2IiwgIiM4NzY5RTciLCAiI0UwMTlFNyIsICIjOUIxOUU3IikKbmFtZXMobXlDb2xvcnMpIDwtIGxldmVscyhwY2FEYXRhJENlbGxMaW5lKQpjb2xTY2FsZSA8LSBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiQ2VsbExpbmUiLHZhbHVlcyA9IG15Q29sb3JzKQpwY2EgPC0gZ2dwbG90KHBjYURhdGEsIGFlcyhQQzEsIFBDMiwgY29sb3I9Q2VsbExpbmUsIHNoYXBlPVJlc2lzdGFuY2UsIGxhYmVsPSIiKSkgKwogIGdlb21fcG9pbnQoc2l6ZT0zKSArCiAgZ2VvbV90ZXh0KGhqdXN0PTAsIHZqdXN0PTApICsKICB4bGFiKHBhc3RlMCgiUEMxOiAiLHBlcmNlbnRWYXJbMV0sIiUgdmFyaWFuY2UiKSkgKwogIHlsYWIocGFzdGUwKCJQQzI6ICIscGVyY2VudFZhclsyXSwiJSB2YXJpYW5jZSIpKSArCiAgY29vcmRfZml4ZWQoKSArCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgY29sU2NhbGUKcGNhCmdnc2F2ZSgiZGVzZXEvb3V0cHV0L3BjYV9hbGxfY2VsbGxpbmVzX2Zvcm1hdHRlZC5zdmciLAogICAgICAgcGxvdCA9IGxhc3RfcGxvdCgpLAogICAgICAgZGV2aWNlID0gInN2ZyIsCiAgICAgICB3aWR0aCA9IDYsCiAgICAgICB1bml0cyA9ICJpbiIpCmBgYAoKIyMgREVTZXEyIC0gQW5hbHlzaXMgb2YgZWFjaCBTZW5zaXRpdmUvUmVzaXN0YW50IHBhaXIKCmBgYHtyIHNlbnNpdGl2ZSByZXNpc3RhbnQgcGFpciwgcmVzdWx0cyA9IEZBTFNFfQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHNwbGl0IDwtIHN0cnNwbGl0KHBhaXIsICJfdnNfIikKICBjb250IDwtIHNwbGl0W1sxXV1bMl0KICBleHAgPC0gc3BsaXRbWzFdXVsxXQogIAogICMgQ3JlYXRlcyBhbm90aGVyIG5vdGVib29rIHRoYXQgc2hhcmVzIHRoZSBzYW1lIGVudmlyb25tZW50CiAgb3V0RmlsZSA8LSBzdHJfaW50ZXJwKCJnZW5lcmF0ZWQtbm90ZWJvb2tzL2Rlc2VxLWFuYWx5c2lzLSR7cGFpcn0uaHRtbCIpCiAgcHJpbnQoc3RyX2ludGVycCgiQ3JlYXRpbmcgbm90ZWJvb2sgZm9yICR7ZXhwfSB2cyAke2NvbnR9IikpCiAgcm1hcmtkb3duOjpyZW5kZXIoJ2Rlc2VxL3NpbmdsZS1jZWxsbGluZS12cy1jb250cm9sLlJtZCcsIAogICAgICAgICAgICAgICAgICAgIG91dHB1dF9maWxlID0gb3V0RmlsZSwgCiAgICAgICAgICAgICAgICAgICAgcGFyYW1zID0gbGlzdChjb250cm9sQ2VsbExpbmUgPSBjb250LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnRhbENlbGxMaW5lID0gZXhwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY291bnRtYXRyaXguYWxsID0gY291bnRtYXRyaXguYWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0YWRhdGEuYWxsID0gbWV0YWRhdGEuYWxsKSkKfQpgYGAKCiMjIyBDb21iaW5lIHJlc3VsdHMgYWNyb3NzIHBhaXJzCgpDb21iaW5lIGRpZmZlcmVudGlhbCBnZW5lIGRhdGEKCmBgYHtyIGNvbWJpbmUgZGVzZXEgcmVzdWx0c30KZnVsbF9yZXN1bHRzX2dlbmVzIDwtIGRhdGEuZnJhbWUoKQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHBhaXJfcmVzdWx0c19maWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X2Rlc2VxX3Jlc3VsdHMuY3N2IikKICBwYWlyX3Jlc3VsdHMgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdihwYWlyX3Jlc3VsdHNfZmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICBwYWlyX3Jlc3VsdHNfZm9ybWF0dGVkIDwtIHBhaXJfcmVzdWx0c1ssIGMoInBhZGoiLCAibG9nMkZvbGRDaGFuZ2UiKV0KICBjb2xuYW1lcyhwYWlyX3Jlc3VsdHNfZm9ybWF0dGVkKSA8LSBjKHN0cl9pbnRlcnAoIiR7cGFpcn1fcGFkaiIpLCBzdHJfaW50ZXJwKCIke3BhaXJ9X2wyZmMiKSkKICBwYWlyX3Jlc3VsdHNfZm9ybWF0dGVkJGdlbmUgPC0gcm93bmFtZXMocGFpcl9yZXN1bHRzX2Zvcm1hdHRlZCkKCiAgaWYgKG5jb2woZnVsbF9yZXN1bHRzX2dlbmVzKSA9PSAwKSB7CiAgICBmdWxsX3Jlc3VsdHNfZ2VuZXMgPSBwYWlyX3Jlc3VsdHNfZm9ybWF0dGVkCiAgfSBlbHNlIHsKICAgIGZ1bGxfcmVzdWx0c19nZW5lcyA8LSBtZXJnZShmdWxsX3Jlc3VsdHNfZ2VuZXMsIHBhaXJfcmVzdWx0c19mb3JtYXR0ZWQsIGJ5ID0gImdlbmUiLCBhbGwgPSBUUlVFKQogIH0KfQoKcm93bmFtZXMoZnVsbF9yZXN1bHRzX2dlbmVzKSA8LSBmdWxsX3Jlc3VsdHNfZ2VuZXMkZ2VuZQpmdWxsX3Jlc3VsdHNfZ2VuZXMgPC0gc3Vic2V0KGZ1bGxfcmVzdWx0c19nZW5lcywgc2VsZWN0ID0gLWMoZ2VuZSkpCgpmb3IgKHJvdyBpbiAxOm5yb3coZnVsbF9yZXN1bHRzX2dlbmVzKSl7CiAgbnVtX3BhaXJzX3NpZ19yZWd1bGF0ZWQgPSAwCiAgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkID0gMAogIG51bV9wYWlyc19zaWdfdXByZWd1bGF0ZWQgPSAwCiAgZm9yIChwYWlyIGluIHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpIHsKICAgIHBhZGogPSBmdWxsX3Jlc3VsdHNfZ2VuZXNbcm93LCBzdHJfaW50ZXJwKCIke3BhaXJ9X3BhZGoiKV0KICAgIGwyZmMgPSBmdWxsX3Jlc3VsdHNfZ2VuZXNbcm93LCBzdHJfaW50ZXJwKCIke3BhaXJ9X2wyZmMiKV0KICAgIGlmICghaXMubmEocGFkaikgJiYgcGFkaiA8IDAuMDUpIHsKICAgICAgaWYgKGwyZmMgPCAwKSB7CiAgICAgICAgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkID0gbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkICsgMQogICAgICB9IGVsc2UgaWYgKGwyZmMgPiAwKSB7CiAgICAgICAgbnVtX3BhaXJzX3NpZ191cHJlZ3VsYXRlZCA9IG51bV9wYWlyc19zaWdfdXByZWd1bGF0ZWQgKyAxCiAgICAgIH0KICAgIH0KICB9CiAgZnVsbF9yZXN1bHRzX2dlbmVzW3JvdywgIm51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkIl0gPSBudW1fcGFpcnNfc2lnX3VwcmVndWxhdGVkCiAgZnVsbF9yZXN1bHRzX2dlbmVzW3JvdywgIm51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQiXSA9IG51bV9wYWlyc19zaWdfZG93bnJlZ3VsYXRlZAogIGZ1bGxfcmVzdWx0c19nZW5lc1tyb3csICJudW1fcGFpcnNfc2lnX3JlZ3VsYXRlZCJdID0gbnVtX3BhaXJzX3NpZ191cHJlZ3VsYXRlZCArIG51bV9wYWlyc19zaWdfZG93bnJlZ3VsYXRlZAp9CgpmdWxsX3Jlc3VsdHNfZ2VuZXMgPSBmdWxsX3Jlc3VsdHNfZ2VuZXMgJT4lIAogIHJlbG9jYXRlKG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQpJT4lIAogIHJlbG9jYXRlKG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkKSAlPiUgCiAgcmVsb2NhdGUobnVtX3BhaXJzX3NpZ19yZWd1bGF0ZWQpCmZ1bGxfcmVzdWx0c19nZW5lcyA9IGZ1bGxfcmVzdWx0c19nZW5lc1tvcmRlcihmdWxsX3Jlc3VsdHNfZ2VuZXMkbnVtX3BhaXJzX3NpZ19yZWd1bGF0ZWQsIGRlY3JlYXNpbmcgPSBUUlVFKSxdCgpwcmludChmdWxsX3Jlc3VsdHNfZ2VuZXMpCndyaXRlLmNzdihmdWxsX3Jlc3VsdHNfZ2VuZXMsIGZpbGUgPSAiZGVzZXEvb3V0cHV0L2RpZmZlcmVudGlhbF9nZW5lX2V4cHJlc3Npb25fYWxsX3NlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMuY3N2IikKYGBgCgpDb21iaW5lIGRpZmZlcmVudGlhbCBwYXRod2F5IGRhdGEKCmBgYHtyfQojIyMgQ29tYmluZSByZXN1bHRzIGFjcm9zcyBwYWlycwpmdWxsX3Jlc3VsdHNfcGF0aHdheXMgPC0gZGF0YS5mcmFtZSgpCmZvciAocGFpciBpbiBzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzKSB7CiAgcGFpcl91cF9wYXRod2F5c19maWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfcGF0aHdheXMuY3N2IikKICBwYWlyX3VwX3BhdGh3YXlzIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5jc3YocGFpcl91cF9wYXRod2F5c19maWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIAogIHBhaXJfZG93bl9wYXRod2F5c19maWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9wYXRod2F5cy5jc3YiKQogIHBhaXJfZG93bl9wYXRod2F5cyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHBhaXJfZG93bl9wYXRod2F5c19maWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIAogIHBhaXJfcGF0aHdheXMgPSByYmluZChwYWlyX3VwX3BhdGh3YXlzLCBwYWlyX2Rvd25fcGF0aHdheXMpCiAgcGFpcl9wYXRod2F5cyRJRCA9IHJvd25hbWVzKHBhaXJfcGF0aHdheXMpCiAgcGFpcl9wYXRod2F5c19mb3JtYXR0ZWQgPSBwYWlyX3BhdGh3YXlzWywgYygiSUQiLCAiRGVzY3JpcHRpb24iLCAicC5hZGp1c3QiLCAiTkVTIildCgogIGNvbG5hbWVzKHBhaXJfcGF0aHdheXNfZm9ybWF0dGVkKSA8LSBjKCJJRCIsICJEZXNjcmlwdGlvbiIsIHN0cl9pbnRlcnAoIiR7cGFpcn1fcGFkaiIpLCBzdHJfaW50ZXJwKCIke3BhaXJ9X05FUyIpKQogIAogIGlmIChuY29sKGZ1bGxfcmVzdWx0c19wYXRod2F5cykgPT0gMCkgewogICAgZnVsbF9yZXN1bHRzX3BhdGh3YXlzID0gcGFpcl9wYXRod2F5c19mb3JtYXR0ZWQKICB9IGVsc2UgewogICAgZnVsbF9yZXN1bHRzX3BhdGh3YXlzIDwtIG1lcmdlKGZ1bGxfcmVzdWx0c19wYXRod2F5cywgcGFpcl9wYXRod2F5c19mb3JtYXR0ZWQsIGJ5ID0gYygiSUQiLCAiRGVzY3JpcHRpb24iKSwgYWxsID0gVFJVRSkKICB9Cn0KCnJvd25hbWVzKGZ1bGxfcmVzdWx0c19wYXRod2F5cykgPC0gZnVsbF9yZXN1bHRzX3BhdGh3YXlzJElECmZ1bGxfcmVzdWx0c19wYXRod2F5cyA8LSBzdWJzZXQoZnVsbF9yZXN1bHRzX3BhdGh3YXlzLCBzZWxlY3QgPSAtYyhJRCkpCgpmb3IgKHJvdyBpbiAxOm5yb3coZnVsbF9yZXN1bHRzX3BhdGh3YXlzKSl7CiAgbnVtX3BhaXJzX3NpZ19yZWd1bGF0ZWQgPSAwCiAgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkID0gMAogIG51bV9wYWlyc19zaWdfdXByZWd1bGF0ZWQgPSAwCiAgZm9yIChwYWlyIGluIHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpIHsKICAgIHBhZGogPSBmdWxsX3Jlc3VsdHNfcGF0aHdheXNbcm93LCBzdHJfaW50ZXJwKCIke3BhaXJ9X3BhZGoiKV0KICAgIE5FUyA9IGZ1bGxfcmVzdWx0c19wYXRod2F5c1tyb3csIHN0cl9pbnRlcnAoIiR7cGFpcn1fTkVTIildCiAgICBpZiAoIWlzLm5hKHBhZGopICYmIHBhZGogPCAwLjA1KSB7CiAgICAgIGlmIChORVMgPCAwKSB7CiAgICAgICAgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkID0gbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkICsgMQogICAgICB9IGVsc2UgaWYgKE5FUyA+IDApIHsKICAgICAgICBudW1fcGFpcnNfc2lnX3VwcmVndWxhdGVkID0gbnVtX3BhaXJzX3NpZ191cHJlZ3VsYXRlZCArIDEKICAgICAgfQogICAgfQogIH0KICBmdWxsX3Jlc3VsdHNfcGF0aHdheXNbcm93LCAibnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQiXSA9IG51bV9wYWlyc19zaWdfdXByZWd1bGF0ZWQKICBmdWxsX3Jlc3VsdHNfcGF0aHdheXNbcm93LCAibnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCJdID0gbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkCiAgZnVsbF9yZXN1bHRzX3BhdGh3YXlzW3JvdywgIm51bV9wYWlyc19zaWdfcmVndWxhdGVkIl0gPSBudW1fcGFpcnNfc2lnX3VwcmVndWxhdGVkICsgbnVtX3BhaXJzX3NpZ19kb3ducmVndWxhdGVkCn0KCmZ1bGxfcmVzdWx0c19wYXRod2F5cyA9IGZ1bGxfcmVzdWx0c19wYXRod2F5cyAlPiUgCiAgcmVsb2NhdGUobnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCklPiUgCiAgcmVsb2NhdGUobnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQpICU+JSAKICByZWxvY2F0ZShudW1fcGFpcnNfc2lnX3JlZ3VsYXRlZCkKZnVsbF9yZXN1bHRzX3BhdGh3YXlzID0gZnVsbF9yZXN1bHRzX3BhdGh3YXlzW29yZGVyKGZ1bGxfcmVzdWx0c19wYXRod2F5cyRudW1fcGFpcnNfc2lnX3JlZ3VsYXRlZCwgZGVjcmVhc2luZyA9IFRSVUUpLF0KCnByaW50KGZ1bGxfcmVzdWx0c19wYXRod2F5cykKd3JpdGUuY3N2KGZ1bGxfcmVzdWx0c19wYXRod2F5cywgZmlsZSA9ICJkZXNlcS9vdXRwdXQvZGlmZmVyZW50aWFsX3BhdGh3YXlzX2FsbF9zZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzLmNzdiIpCmBgYAoKIyMjIFBsb3QgZGlmZmVyZW50aWFsIHBhdGh3YXlzCgpVc2UgUkVWSUdPIHRvIGNsdXN0ZXIgcGF0aHdheXMKCmBgYHtyfQoKc2ltaWxhcml0eV9tYXRyaXggPSBjYWxjdWxhdGVTaW1NYXRyaXgocm93bmFtZXMoZnVsbF9yZXN1bHRzX3BhdGh3YXlzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnZGIgPSAib3JnLkhzLmVnLmRiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb250ID0gIkJQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIlJlbCIpCgpzY29yZXMgPSByZXAoMSwgbmNvbChzaW1pbGFyaXR5X21hdHJpeCkpCm5hbWVzKHNjb3JlcykgPSBjb2xuYW1lcyhzaW1pbGFyaXR5X21hdHJpeCkKcmVkdWNlZFRlcm1zID0gcmVkdWNlU2ltTWF0cml4KHNpbWlsYXJpdHlfbWF0cml4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NvcmVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkID0gMC44LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JnZGIgPSAib3JnLkhzLmVnLmRiIikKYGBgCgpQbG90IGNvbnNpc3RlbnRseSBkaWZmZXJlbnRpYWwgcGF0aHdheXMgZm9yIGFsbCBjZWxsbGluZXMgaW4gb25lIHBsb3QKCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9OH0KZGFya2VuX2NvbG9yIDwtIGZ1bmN0aW9uKGNvbG9yLCBmYWN0b3IgPSAwLjUpIHsKICAjIENvbnZlcnQgY29sb3IgdG8gUkdCCiAgcmdiX3ZhbHMgPC0gY29sMnJnYihjb2xvcikgLyAyNTUKICAjIERhcmtlbiBlYWNoIFJHQiBjaGFubmVsCiAgZGFya2VuZWRfdmFscyA8LSByZ2JfdmFscyAqIGZhY3RvcgogICMgRW5zdXJlIHZhbHVlcyBhcmUgd2l0aGluIHZhbGlkIHJhbmdlIFswLCAxXQogIGRhcmtlbmVkX3ZhbHMgPC0gcG1pbihwbWF4KGRhcmtlbmVkX3ZhbHMsIDApLCAxKQogICMgQ29udmVydCBiYWNrIHRvIGhleGFkZWNpbWFsIGNvbG9yCiAgZGFya2VuZWRfY29sb3IgPC0gcmdiKGRhcmtlbmVkX3ZhbHNbMV0sIGRhcmtlbmVkX3ZhbHNbMl0sIGRhcmtlbmVkX3ZhbHNbM10sIG1heENvbG9yVmFsdWUgPSAyNTUpCiAgcmV0dXJuKGRhcmtlbmVkX2NvbG9yKQp9CgoKZm9ybWF0X3BhdGh3YXlfcmVzdWx0cyA9IGZ1bGxfcmVzdWx0c19wYXRod2F5cwojIFN1YnNldCB0byB0ZXJtcyBhYmxlIHRvIGJlIGNsdXN0ZXJlZApmb3JtYXRfcGF0aHdheV9yZXN1bHRzID0gZm9ybWF0X3BhdGh3YXlfcmVzdWx0c1tyb3duYW1lcyhmb3JtYXRfcGF0aHdheV9yZXN1bHRzKSAlaW4lIHJvd25hbWVzKHJlZHVjZWRUZXJtcyksXQpmb3JtYXRfcGF0aHdheV9yZXN1bHRzID0gZm9ybWF0X3BhdGh3YXlfcmVzdWx0c1ssLSB3aGljaChjb2xuYW1lcyhmb3JtYXRfcGF0aHdheV9yZXN1bHRzKSAlaW4lIGMoIm51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQiLCAibnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQiKSldCgojIENoYW5nZSBmb3JtYXR0aW5nIG9mIGNvbHVtbiBuYW1lcyBzbyB0aGF0IGl0IGlzIGVhc2llciBmb3IgcGl2b3QgZnVuY3Rpb24gYmVsb3cKY29sbmFtZXMoZm9ybWF0X3BhdGh3YXlfcmVzdWx0cykgPSBjb2xuYW1lcyhmb3JtYXRfcGF0aHdheV9yZXN1bHRzKSAlPiUKICBtYXBfY2hyKFwoeCkgc3ViKCJfKD89W15fXSokKSIsICItIiwgeCwgcGVybCA9IFRSVUUpKQoKIyBTZXQgcGFyZW50IHRlcm1zCmZvcm1hdF9wYXRod2F5X3Jlc3VsdHMkcGFyZW50UGF0aHdheUlkID0gcm93bmFtZXMoZm9ybWF0X3BhdGh3YXlfcmVzdWx0cykgJT4lCiAgbWFwX2NocihcKHgpIHJlZHVjZWRUZXJtc1tyb3duYW1lcyhyZWR1Y2VkVGVybXMpID09IHgsICJwYXJlbnQiXSkKZm9ybWF0X3BhdGh3YXlfcmVzdWx0cyRwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24gPSBmb3JtYXRfcGF0aHdheV9yZXN1bHRzJHBhcmVudFBhdGh3YXlJZCAlPiUKICBtYXBfY2hyKFwoeCkgcGFzdGUwKGZvcm1hdF9wYXRod2F5X3Jlc3VsdHNbcm93bmFtZXMoZm9ybWF0X3BhdGh3YXlfcmVzdWx0cykgPT0geCwgIkRlc2NyaXB0aW9uIl0sICIgKCIsIG5yb3coZm9ybWF0X3BhdGh3YXlfcmVzdWx0c1tmb3JtYXRfcGF0aHdheV9yZXN1bHRzJHBhcmVudFBhdGh3YXlJZCA9PSB4LF0pLCAiKSIpKQoKcGl2b3RfY29scyA9IGNvbG5hbWVzKGZvcm1hdF9wYXRod2F5X3Jlc3VsdHMpCnBpdm90X2NvbHMgPSBwaXZvdF9jb2xzWyEocGl2b3RfY29scyAlaW4lIGMoIkRlc2NyaXB0aW9uIiwgIm51bV9wYWlyc19zaWctcmVndWxhdGVkIiwgInBhcmVudFBhdGh3YXlJZCIsICJwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24iKSldCnBsb3RfZGF0YSA9IHBpdm90X2xvbmdlcihkYXRhID0gZm9ybWF0X3BhdGh3YXlfcmVzdWx0cywKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHMgPSBwaXZvdF9jb2xzLAogICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSBjKCJjZWxsbGluZVBhaXIiLCAiLnZhbHVlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc19zZXAgPSAiLSIpCgojIFNjb3JlIHRoZSB0ZXJtIGNhdGVnb3JpZXMgYnkgY29tYmluaW5nIHNjb3JlcyBhY3Jvc3MgYWxsIGNoaWxkIHBhdGh3YXlzIGFuZCBhbGwgY2VsbGxpbmUgcGFpcnMKcGxvdF9kYXRhJHNjb3JlID0gcGxvdF9kYXRhJE5FUwojIHBsb3RfZGF0YSRzY29yZSA9IC1sb2cxMChwbG90X2RhdGEkcGFkaikgKiBwbG90X2RhdGEkTkVTCnBsb3RfZGF0YSA9IHBsb3RfZGF0YSAlPiUKICBncm91cF9ieShwYXJlbnRQYXRod2F5SWQpICU+JQogIG11dGF0ZShjYXRlZ29yeV9zY29yZSA9IG1lYW4oc2NvcmUsIG5hLnJtID0gVFJVRSkpCgojIE9yZGVyIHRlcm1zIGJ5IHRoZSBzY29yZSBvZiB0aGUgcGFyZW50IHBhdGh3YXkKcGxvdF9kYXRhID0gcGxvdF9kYXRhICU+JSBhcnJhbmdlKGNhdGVnb3J5X3Njb3JlLCBzY29yZSkKcGxvdF9kYXRhJHBhcmVudFBhdGh3YXlEZXNjcmlwdGlvbiA8LSBmYWN0b3IocGxvdF9kYXRhJHBhcmVudFBhdGh3YXlEZXNjcmlwdGlvbiwgbGV2ZWxzID0gdW5pcXVlKHBsb3RfZGF0YSRwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24pKQoKIyBTaGlmdCBORVMgc28gdGhhdCB0aGUgLTEgdG8gMSByZWdpb24gZG9lc24ndCBnbyB1bnVzZWQKaWYgKGFueShhYnMocGxvdF9kYXRhJE5FUykpIDwgMSkgewogIHByaW50KCJFUlJPUjogVGhpcyBwbG90IHdpbGwgYmUgbWVzc2VkIHVwIGJlY2F1c2Ugd2UgYXNzdW1lZCBub3ZhbHVlcyBvZiBORVMgYmV0d2VlbiAtMSBhbmQgMS4iKQp9CiMgcGxvdF9kYXRhJHNoaWZ0X05FUyA9IHBsb3RfZGF0YSRORVMgJT4lCiMgICBtYXBfZGJsKFwoeCkgaWZlbHNlKHggPiAxLCB4IC0gMSwgaWZlbHNlKHggPCAtMSwgeCArIDEsIHgpKSkKCmdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gTkVTLCB5ID0gcGFyZW50UGF0aHdheURlc2NyaXB0aW9uLCBzaXplID0gLWxvZzEwKHBhZGopLCBjb2xvciA9IGNlbGxsaW5lUGFpcikpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKGhlaWdodCA9IDAuMSksIGFscGhhID0gMC41LCBsYXllciA9ICJhYm92ZSIpICsgICMgQWRkIGppdHRlciBhbmQgcmVkdWNlIHBvaW50IHRyYW5zcGFyZW5jeQogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSwgNSkpICsKICAjIENvbG9yaW5nIGJ5IGluIHZpdm8vaW4gdml0cm8gcmVzaXN0YW5jZSAoT1ZDQVJzIGJvdGggaW4gdml0cm87IFBFTyBhbmQgUEVBcyBpbiB2aXZvKQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjRjcxMzAwIiwgIiNFN0NGMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjRjU1QUZBIiwgIiNCRDgxNjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMTFEQkNDIiwgIiMwRTU4RTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMTRCNzBBIikpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBzZXFfYWxvbmcobGV2ZWxzKHBsb3RfZGF0YSRwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24pKSwgY29sb3IgPSAiZ3JheSIsIHNpemUgPSAwLjEsIGxheWVyID0gImJlbG93IikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKwoKICBsYWJzKHggPSAnTm9ybWFsaXplZCBFbnJpY2htZW50IFNjb3JlJywgeSA9ICdQYXRod2F5IENhdGVnb3J5ICgjIHBhdGh3YXlzKScsIGNvbG9yID0gJ0NlbGxsaW5lIFBhaXInKSArCiAgZ2d0aXRsZSgnUGF0aHdheSByZWd1bGF0aW9uIGluIHJlc2lzdGFudCBjZWxsbGluZXMgKGFzIGNvbXBhcmVkIHRvIGNvcnJlc3BvbmRpbmcgc2Vuc2l0aXZlIGNlbGxsaW5lKScpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZV9jbGFzc2ljKCkKCmdnc2F2ZSgiZGVzZXEvb3V0cHV0L2RpZmZlcmVudGlhbF9wYXRod2F5c19hbGxfY2VsbGxpbmVzX3NpbWlsYXJpdHlfdGhyZXNob2xkXzAuOC5zdmciLAogICAgICAgcGxvdCA9IGxhc3RfcGxvdCgpLAogICAgICAgZGV2aWNlID0gInN2ZyIsCiAgICAgICB3aWR0aCA9IDE1LAogICAgICAgaGVpZ2h0ID0gMTAsCiAgICAgICB1bml0cyA9ICJpbiIpCgojIyBwbG90IGluIHZpdHJvIHJlc2lzdGFuY2Ugb25seQpnZ3Bsb3QocGxvdF9kYXRhW3Bsb3RfZGF0YSRjZWxsbGluZVBhaXIgJWluJSBjKCJPVkNBUjRCX3ZzX09WQ0FSNCIsICJPVkNBUjRBX3ZzX09WQ0FSNCIsICJPVkNBUjNBX3ZzX09WQ0FSMyIsICJPVkNBUjNCX3ZzX09WQ0FSMyIpLF0sIGFlcyh4ID0gTkVTLCB5ID0gcGFyZW50UGF0aHdheURlc2NyaXB0aW9uLCBzaXplID0gLWxvZzEwKHBhZGopLCBjb2xvciA9IGNlbGxsaW5lUGFpcikpICsKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKGhlaWdodCA9IDAuMSksIGFscGhhID0gMC41LCBsYXllciA9ICJhYm92ZSIpICsgICMgQWRkIGppdHRlciBhbmQgcmVkdWNlIHBvaW50IHRyYW5zcGFyZW5jeQogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSwgNSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI0Y3MTMwMCIsICIjRTdDRjAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI0Y1NUFGQSIsICIjQkQ4MTYxIikpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBzZXFfYWxvbmcobGV2ZWxzKHBsb3RfZGF0YSRwYXJlbnRQYXRod2F5RGVzY3JpcHRpb24pKSwgY29sb3IgPSAiZ3JheSIsIHNpemUgPSAwLjEsIGxheWVyID0gImJlbG93IikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIikgKwoKICBsYWJzKHggPSAnTm9ybWFsaXplZCBFbnJpY2htZW50IFNjb3JlJywgeSA9ICdQYXRod2F5IENhdGVnb3J5ICgjIHBhdGh3YXlzKScsIGNvbG9yID0gJ0NlbGxsaW5lIFBhaXInKSArCiAgZ2d0aXRsZSgnUGF0aHdheSByZWd1bGF0aW9uIGluIGluLXZpdHJvIGRlcml2ZWQgcmVzaXN0YW50IGNlbGxsaW5lcycpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICB0aGVtZV9jbGFzc2ljKCkKCiMjIHBsb3QgaW4gdml2byByZXNpc3RhbmNlIG9ubHkKZ2dwbG90KHBsb3RfZGF0YVtwbG90X2RhdGEkY2VsbGxpbmVQYWlyICVpbiUgYygiUEVPNF92c19QRU8yIiwgIlBFTzZfdnNfUEVPMiIsICJQRUEyX3ZzX1BFQTEiKSxdLCBhZXMoeCA9IE5FUywgeSA9IHBhcmVudFBhdGh3YXlEZXNjcmlwdGlvbiwgc2l6ZSA9IC1sb2cxMChwYWRqKSwgY29sb3IgPSBjZWxsbGluZVBhaXIpKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcihoZWlnaHQgPSAwLjEpLCBhbHBoYSA9IDAuNSwgbGF5ZXIgPSAiYWJvdmUiKSArICAjIEFkZCBqaXR0ZXIgYW5kIHJlZHVjZSBwb2ludCB0cmFuc3BhcmVuY3kKICBzY2FsZV9zaXplX2NvbnRpbnVvdXMocmFuZ2UgPSBjKDEsIDUpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiMxMURCQ0MiLCAiIzBFNThFNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiMxNEI3MEEiKSkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IHNlcV9hbG9uZyhsZXZlbHMocGxvdF9kYXRhJHBhcmVudFBhdGh3YXlEZXNjcmlwdGlvbikpLCBjb2xvciA9ICJncmF5Iiwgc2l6ZSA9IDAuMSwgbGF5ZXIgPSAiYmVsb3ciKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiYmxhY2siKSArCgogIGxhYnMoeCA9ICdOb3JtYWxpemVkIEVucmljaG1lbnQgU2NvcmUnLCB5ID0gJ1BhdGh3YXkgQ2F0ZWdvcnkgKCMgcGF0aHdheXMpJywgY29sb3IgPSAnQ2VsbGxpbmUgUGFpcicpICsKICBnZ3RpdGxlKCdQYXRod2F5IHJlZ3VsYXRpb24gaW4gaW4tdml2byBkZXJpdmVkIHJlc2lzdGFudCBjZWxsbGluZXMnKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgdGhlbWVfY2xhc3NpYygpCgpgYGAKIyMjIFBsb3QgZGlmZmVyZW50aWFsIHBhdGh3YXlzCgpQbG90IHNob3dzIGhvdyB0aGUgc3ByZWFkIG9mIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsIHBhdGh3YXlzIGFjcm9zcyBzZW5zaXRpdmUvcmVzaXN0YW50IHBhaXJzLgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0KcGxvdF9kYXRhID0gZnVsbF9yZXN1bHRzX3BhdGh3YXlzWyxjKCJudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCIsICJudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkIiwgIkRlc2NyaXB0aW9uIildCnByaW50KHBsb3RfZGF0YSkKIyBPcmRlcnMgcHJpbWFyaWx5IGJ5IGhvdyBtYW55IG1vcmUgcGFpcnMgd2VyZSBzaWcgdXAgdGhhbiBzaWcgZG93biByZWd1bGF0ZWQKIyBPcmRlcnMgc2Vjb25kYXJpbHkgYnkgaG93IGZldyBwYWlycyB3ZXJlIHJlZ3VsYXRlZCBpbiB0aGUgbm9uLWRvbWluYW50IGRpcmVjdGlvbgojIFRoaXMgb3JkZXJpbmcgZG9lcyB0aGUgZm9sbG93aW5nOiAoNywgMCkgPiAoNiwgMCkgPiAoNSwgMCkgPiAoNiwgMSkgPiAoNywgMikKcGxvdF9kYXRhIDwtIHBsb3RfZGF0YSAlPiUKICBtdXRhdGUob3JkZXIgPSBpZmVsc2UobnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQgPiBudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkLAogICAgICAgICAgICAgICAgICAgICAgICBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCAtIG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQgKiAxLjAxLAogICAgICAgICAgICAgICAgICAgICAgICAtMSAqIChudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkIC0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQgKiAxLjAxKSkpCgpwbG90X2RhdGEgPC0gcGxvdF9kYXRhICU+JQogIGFycmFuZ2UoZGVzYyhvcmRlcikpCgpwbG90X2RhdGEgPSBwbG90X2RhdGEgJT4lCiAgbXV0YXRlKHBhbmVsID0gaWZlbHNlKG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkID4gbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwKICAgICAgICAgICAgICAgICAgICAgICAgIm92ZXJhbGwgdXByZWd1bGF0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UobnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCA9PSBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJldmVubHkgdXAgYW5kIGRvd24gcmVndWxhdGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvdmVyYWxsIGRvd25yZWd1bGF0ZWQiKSkpCnBsb3RfZGF0YSRwYW5lbCA9IGZhY3RvcihwbG90X2RhdGEkcGFuZWwsIGxldmVscyA9IGMoIm92ZXJhbGwgdXByZWd1bGF0ZWQiLCAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsICJvdmVyYWxsIGRvd25yZWd1bGF0ZWQiKSkKCiMgb3JnYW5pemUgdGhlIHggY29vcmRpbmF0ZXMgYmFzZWQgb24gcGFuZWwKcGxvdF9kYXRhJGxlZnRfcGxhY2VtZW50ID0gMDoobnJvdyhwbG90X2RhdGEpIC0gMSkKZXF1YWxfcGFuZWxfc3RhcnQgPSBtaW4ocGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsICJsZWZ0X3BsYWNlbWVudCJdKQpwbG90X2RhdGFbcGxvdF9kYXRhJHBhbmVsID09ICJldmVubHkgdXAgYW5kIGRvd24gcmVndWxhdGVkIixdJGxlZnRfcGxhY2VtZW50ID0gcGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCAtIGVxdWFsX3BhbmVsX3N0YXJ0CmRvd25fcGFuZWxfc3RhcnQgPSBtaW4ocGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAib3ZlcmFsbCBkb3ducmVndWxhdGVkIiwgImxlZnRfcGxhY2VtZW50Il0pCnBsb3RfZGF0YVtwbG90X2RhdGEkcGFuZWwgPT0gIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCA9IHBsb3RfZGF0YVtwbG90X2RhdGEkcGFuZWwgPT0gIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCAtIGRvd25fcGFuZWxfc3RhcnQKCnBsb3RfZGF0YSRjZW50ZXJfcGxhY2VtZW50ID0gcGxvdF9kYXRhJGxlZnRfcGxhY2VtZW50ICsgMC41CgojIENyZWF0ZSBiYXIgcGxvdApnZ3Bsb3QocGxvdF9kYXRhKSArCiAgZmFjZXRfd3JhcCh+cGFuZWwsIG5jb2wgPSAzKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gLTEgKiBudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkLCBmaWxsID0gImRvd25yZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX2JhcihhZXMoeCA9IGNlbnRlcl9wbGFjZW1lbnQsIHkgPSBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCwgZmlsbCA9ICJ1cHJlZ3VsYXRpb24iLCB3aWR0aCA9IDEpLCBzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC1tYXgocGxvdF9kYXRhJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQpIC0xLCBtYXgocGxvdF9kYXRhJG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkKSkgKzEpICsKICB0aGVtZV9taW5pbWFsKCkgKyAgIyBBcHBseSBhIG1pbmltYWwgdGhlbWUKICBsYWJzKHggPSAiUGF0aHdheXMiLCB5ID0gIiMgcmVzaXN0YW50IGNlbGxsaW5lcyBzaG93aW5nIHNpZ25pZmljYW50IHJlZ3VsYXRpb24gb2YgcGF0aHdheSIpICsgIyBTZXQgYXhpcyBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ1cHJlZ3VsYXRpb24iID0gImdyZWVuIiwgImRvd25yZWd1bGF0aW9uIiA9ICJyZWQiKSkgKyAjIFNldCBmaWxsIGNvbG9ycwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGdndGl0bGUoJ1BhdGh3YXkgcmVndWxhdGlvbiBpbiByZXNpc3RhbnQgY2VsbGxpbmVzIChhcyBjb21wYXJlZCB0byBjb3JyZXNwb25kaW5nIHNlbnNpdGl2ZSBjZWxsbGluZSknKQogIAoKZ2dzYXZlKCJkZXNlcS9vdXRwdXQvY29uc2lzdGVudGx5X2RpZmZlcmVudGlhbF9wYXRod2F5c19hbGxfY2VsbGxpbmVzLnN2ZyIsCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCksCiAgICAgICBkZXZpY2UgPSAic3ZnIiwKICAgICAgIHdpZHRoID0gMTAsCiAgICAgICBoZWlnaHQgPSA1LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCkNsb3NlIHVwIG9mIGNvbnNpc3RlbnRseSB1cHJlZ3VsYXRlZCBwYXRod2F5cwoKYGBge3IsIGZpZ3VyZS53aWR0aCA9IDEwLCBmaWd1cmUuaGVpZ2h0ID0gNX0KIyBFbmQgdGhlIHBsb3QgYmV0d2VlbiAiY2F0ZWdvcmllcyIgb2YgcGF0aHdheXMsIGluY2x1ZGluZyBsZXNzIHRoYW4gMTAwIHBhdGh3YXlzIHRvdGFsCmVuZF9pbmRleCA9IDEKZm9yKGkgaW4gMToxMDApIHsKICBpZiAocGxvdF9kYXRhJG9yZGVyW2ldICE9IHBsb3RfZGF0YSRvcmRlcltpKzFdKSB7CiAgICBlbmRfaW5kZXggPSBpCiAgfQp9CgpwbG90X2RhdGFfdXAgPSBwbG90X2RhdGFbMTplbmRfaW5kZXgsXQpwbG90X2RhdGFfdXAkcGF0aHdheSA9IHBsb3RfZGF0YV91cCREZXNjcmlwdGlvbgoKcHJpbnQocGxvdF9kYXRhX3VwKQoKZ2dwbG90KHBsb3RfZGF0YV91cCkgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gMSwgbGFiZWwgPSBwYXRod2F5KSwgc2l6ZSA9IDIsIGNvbG9yID0gImJsYWNrIiwgYW5nbGUgPSA5MCkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC1tYXgocGxvdF9kYXRhX3VwJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQpIC0xLCBtYXgocGxvdF9kYXRhX3VwJG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkKSkgKzEpICsKICB0aGVtZV9taW5pbWFsKCkgKyAgIyBBcHBseSBhIG1pbmltYWwgdGhlbWUKICBsYWJzKHggPSAiUGF0aHdheXMiLCB5ID0gIiMgcmVzaXN0YW50IGNlbGxsaW5lcyBzaG93aW5nIHNpZ25pZmljYW50IHJlZ3VsYXRpb24gb2YgcGF0aHdheSIpICsgIyBTZXQgYXhpcyBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ1cHJlZ3VsYXRpb24iID0gImdyZWVuIiwgImRvd25yZWd1bGF0aW9uIiA9ICJyZWQiKSkgKyAjIFNldCBmaWxsIGNvbG9ycwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGdndGl0bGUoJ1pvb21pbmcgaW4gb24gbGVmdCBoYW5kIHNpZGUgKGV4dHJlbWVseSBjb25zaXN0ZW50bHkgdXByZWd1bGF0ZWQgcGF0aHdheXMpJykKCmdnc2F2ZSgiZGVzZXEvb3V0cHV0L2NvbnNpc3RlbnRseV91cHJlZ3VsYXRlZF9wYXRod2F5c19hbGxfY2VsbGxpbmVzLnN2ZyIsCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCksCiAgICAgICBkZXZpY2UgPSAic3ZnIiwKICAgICAgIHdpZHRoID0gMTAsCiAgICAgICBoZWlnaHQgPSA1LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCkNsb3NlIHVwIG9mIGNvbnNpc3RlbnRseSBkb3ducmVndWxhdGVkIHBhdGh3YXlzCgpgYGB7ciwgZmlndXJlLndpZHRoID0gMTAsIGZpZ3VyZS5oZWlnaHQgPSA1fQojIEVuZCB0aGUgcGxvdCBiZXR3ZWVuICJjYXRlZ29yaWVzIiBvZiBwYXRod2F5cywgaW5jbHVkaW5nIGxlc3MgdGhhbiAxMDAgcGF0aHdheXMgdG90YWwKc3RhcnRfaW5kZXggPSAxCmZvcihpIGluIG5yb3cocGxvdF9kYXRhKToobnJvdyhwbG90X2RhdGEpIC0gOTkpKSB7CiAgaWYgKHBsb3RfZGF0YSRvcmRlcltpXSAhPSBwbG90X2RhdGEkb3JkZXJbaS0xXSkgewogICAgc3RhcnRfaW5kZXggPSBpCiAgfQp9CgpwbG90X2RhdGFfZG93biA9IHBsb3RfZGF0YVtzdGFydF9pbmRleDpucm93KHBsb3RfZGF0YSksXQpwbG90X2RhdGFfZG93biRjZW50ZXJfcGxhY2VtZW50ID0gcGxvdF9kYXRhX2Rvd24kY2VudGVyX3BsYWNlbWVudCAtIHN0YXJ0X2luZGV4ICsgMSAjIFN0YXJ0IGF0IDAKcGxvdF9kYXRhX2Rvd24kcGF0aHdheSA9IHBsb3RfZGF0YV9kb3duJERlc2NyaXB0aW9uCgpwcmludChwbG90X2RhdGFfZG93bikKCmdncGxvdChwbG90X2RhdGFfZG93bikgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gLTEsIGxhYmVsID0gcGF0aHdheSksIHNpemUgPSAyLCBjb2xvciA9ICJibGFjayIsIGFuZ2xlID0gOTApICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtbWF4KHBsb3RfZGF0YV9kb3duJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQpIC0xLCBtYXgocGxvdF9kYXRhX2Rvd24kbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQpKSArMSkgKwogIHRoZW1lX21pbmltYWwoKSArICAjIEFwcGx5IGEgbWluaW1hbCB0aGVtZQogIGxhYnMoeCA9ICJQYXRod2F5cyIsIHkgPSAiIyByZXNpc3RhbnQgY2VsbGxpbmVzIHNob3dpbmcgc2lnbmlmaWNhbnQgcmVndWxhdGlvbiBvZiBwYXRod2F5IikgKyAjIFNldCBheGlzIGxhYmVscwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInVwcmVndWxhdGlvbiIgPSAiZ3JlZW4iLCAiZG93bnJlZ3VsYXRpb24iID0gInJlZCIpKSArICMgU2V0IGZpbGwgY29sb3JzCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgZ2d0aXRsZSgnWm9vbWluZyBpbiBvbiByaWdodCBoYW5kIHNpZGUgKGV4dHJlbWVseSBjb25zaXN0ZW50bHkgZG93bnJlZ3VsYXRlZCBwYXRod2F5cyknKQoKZ2dzYXZlKCJkZXNlcS9vdXRwdXQvY29uc2lzdGVudGx5X2Rvd25yZWd1bGF0ZWRfcGF0aHdheXNfYWxsX2NlbGxsaW5lcy5zdmciLAogICAgICAgcGxvdCA9IGxhc3RfcGxvdCgpLAogICAgICAgZGV2aWNlID0gInN2ZyIsCiAgICAgICB3aWR0aCA9IDEwLAogICAgICAgaGVpZ2h0ID0gNSwKICAgICAgIHVuaXRzID0gImluIikKYGBgCiMjIyBQbG90IGRpZmZlcmVudGlhbCBnZW5lcwoKUGxvdCBzaG93cyBob3cgdGhlIHNwcmVhZCBvZiBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbCBnZW5lcyBhY3Jvc3Mgc2Vuc2l0aXZlL3Jlc2lzdGFudCBwYWlycy4KCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDV9CiMgZm9ybWF0dGVkX2dlbmVfcmVzdWx0cyA9IGZ1bGxfcmVzdWx0c19nZW5lc1ssYygibnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQiLCAibnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCIpXQoKcGxvdF9kYXRhID0gZnVsbF9yZXN1bHRzX2dlbmVzWyxjKCJudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCIsICJudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkIildCgojIHBsb3RfZGF0YSA8LSBmdWxsX3Jlc3VsdHNfZ2VuZXMgJT4lCiMgICBncm91cF9ieShudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCwgbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCkgJT4lCiMgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgojIE9yZGVycyBwcmltYXJpbHkgYnkgaG93IG1hbnkgbW9yZSBwYWlycyB3ZXJlIHNpZyB1cCB0aGFuIHNpZyBkb3duIHJlZ3VsYXRlZAojIE9yZGVycyBzZWNvbmRhcmlseSBieSBob3cgZmV3IHBhaXJzIHdlcmUgcmVndWxhdGVkIGluIHRoZSBub24tZG9taW5hbnQgZGlyZWN0aW9uCiMgVGhpcyBvcmRlcmluZyBkb2VzIHRoZSBmb2xsb3dpbmc6ICg3LCAwKSA+ICg2LCAwKSA+ICg1LCAwKSA+ICg2LCAxKSA+ICg3LCAyKQpwbG90X2RhdGEgPC0gcGxvdF9kYXRhICU+JQogIG11dGF0ZShvcmRlciA9IGlmZWxzZShudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCA+IG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQsCiAgICAgICAgICAgICAgICAgICAgICAgIG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkIC0gbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCAqIDEuMDEsCiAgICAgICAgICAgICAgICAgICAgICAgIC0xICogKG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQgLSBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCAqIDEuMDEpKSkKCnBsb3RfZGF0YSA8LSBwbG90X2RhdGEgJT4lCiAgYXJyYW5nZShkZXNjKG9yZGVyKSkKCnBsb3RfZGF0YSA9IHBsb3RfZGF0YSAlPiUKICBtdXRhdGUocGFuZWwgPSBpZmVsc2UobnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQgPiBudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAib3ZlcmFsbCB1cHJlZ3VsYXRlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkID09IG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImV2ZW5seSB1cCBhbmQgZG93biByZWd1bGF0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIpKSkKcGxvdF9kYXRhJHBhbmVsID0gZmFjdG9yKHBsb3RfZGF0YSRwYW5lbCwgbGV2ZWxzID0gYygib3ZlcmFsbCB1cHJlZ3VsYXRlZCIsICJldmVubHkgdXAgYW5kIGRvd24gcmVndWxhdGVkIiwgIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIpKQoKIyBBZGQgYSBzbWFsbCBhbW91bnQgb2YgY29sb3IgaWYgbm90IHJlZ3VsYXRlZAp1bnJlZ3VsYXRlZCA9IHBsb3RfZGF0YSRudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCA9PSAwICYgcGxvdF9kYXRhJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQgPT0gMApwbG90X2RhdGFbdW5yZWd1bGF0ZWQsXSRudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCA9IDAuMDA1CnBsb3RfZGF0YVt1bnJlZ3VsYXRlZCxdJG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQgPSAtMC4wMDUKCiMgb3JnYW5pemUgdGhlIHggY29vcmRpbmF0ZXMgYmFzZWQgb24gcGFuZWwKcGxvdF9kYXRhJGxlZnRfcGxhY2VtZW50ID0gMDoobnJvdyhwbG90X2RhdGEpIC0gMSkKZXF1YWxfcGFuZWxfc3RhcnQgPSBtaW4ocGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsICJsZWZ0X3BsYWNlbWVudCJdKQpwbG90X2RhdGFbcGxvdF9kYXRhJHBhbmVsID09ICJldmVubHkgdXAgYW5kIGRvd24gcmVndWxhdGVkIixdJGxlZnRfcGxhY2VtZW50ID0gcGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAiZXZlbmx5IHVwIGFuZCBkb3duIHJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCAtIGVxdWFsX3BhbmVsX3N0YXJ0CmRvd25fcGFuZWxfc3RhcnQgPSBtaW4ocGxvdF9kYXRhW3Bsb3RfZGF0YSRwYW5lbCA9PSAib3ZlcmFsbCBkb3ducmVndWxhdGVkIiwgImxlZnRfcGxhY2VtZW50Il0pCnBsb3RfZGF0YVtwbG90X2RhdGEkcGFuZWwgPT0gIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCA9IHBsb3RfZGF0YVtwbG90X2RhdGEkcGFuZWwgPT0gIm92ZXJhbGwgZG93bnJlZ3VsYXRlZCIsXSRsZWZ0X3BsYWNlbWVudCAtIGRvd25fcGFuZWxfc3RhcnQKCnBsb3RfZGF0YSRjZW50ZXJfcGxhY2VtZW50ID0gcGxvdF9kYXRhJGxlZnRfcGxhY2VtZW50ICsgMC41CgojIG5leHRfcGxhY2VtZW50ID0gMAojIGZvcihyb3cgaW4gMTpucm93KHBsb3RfZGF0YSkpIHsKIyAgIG5ld19wbGFjZW1lbnQgPSBuZXh0X3BsYWNlbWVudAojICAgcGxvdF9kYXRhW3JvdywgImxlZnRfcGxhY2VtZW50Il0gPSBuZXdfcGxhY2VtZW50CiMgICBuZXh0X3BsYWNlbWVudCA9IG5ld19wbGFjZW1lbnQgKyBwbG90X2RhdGFbcm93LCAiY291bnQiXQojIH0KCiMgTmVlZCB0aGUgY2VudGVyIG9mIHRoZSBiYXIgZm9yIHBsb3R0aW5nIHB1cnBvc2VzCiMgcGxvdF9kYXRhJGNlbnRlcl9wbGFjZW1lbnQgPSBwbG90X2RhdGEkbGVmdF9wbGFjZW1lbnQgKyBwbG90X2RhdGEkY291bnQvMgoKcHJpbnQocGxvdF9kYXRhKQoKIyBDcmVhdGUgYmFyIHBsb3QKZ2dwbG90KHBsb3RfZGF0YSkgKwogIGZhY2V0X3dyYXAofnBhbmVsLCBuY29sID0gMywgc2NhbGVzID0gImZpeGVkIikgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAjIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gY291bnQpLCBzdGF0ID0gImlkZW50aXR5IikgKwogICMgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSBjb3VudCksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLW1heChwbG90X2RhdGEkbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCkgLTEsIG1heChwbG90X2RhdGEkbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQpKSArMSkgKwogICMgU2V0IHktYXhpcyBsaW1pdHMKICAjIGNvb3JkX2ZsaXAoKSArICAjIEZsaXAgdGhlIGNvb3JkaW5hdGVzIHRvIGNyZWF0ZSBhIHNpZGV3YXlzIHBsb3QKICB0aGVtZV9taW5pbWFsKCkgKyAgIyBBcHBseSBhIG1pbmltYWwgdGhlbWUKICBsYWJzKHggPSAiR2VuZXMiLCB5ID0gIiMgcmVzaXN0YW50IGNlbGxsaW5lcyBzaG93aW5nIHNpZ25pZmljYW50IHJlZ3VsYXRpb24gb2YgZ2VuZSIpICsgIyBTZXQgYXhpcyBsYWJlbHMKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ1cHJlZ3VsYXRpb24iID0gImdyZWVuIiwgImRvd25yZWd1bGF0aW9uIiA9ICJyZWQiKSkgKyAjIFNldCBmaWxsIGNvbG9ycwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgICAjIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgIyBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGdndGl0bGUoJ0dlbmUgcmVndWxhdGlvbiBpbiByZXNpc3RhbnQgY2VsbGxpbmVzIChhcyBjb21wYXJlZCB0byBjb3JyZXNwb25kaW5nIHNlbnNpdGl2ZSBjZWxsbGluZSknKQogIAoKZ2dzYXZlKCJkZXNlcS9vdXRwdXQvZGlmZmVyZW50aWFsX2dlbmVzX2FsbF9jZWxsbGluZXMuc3ZnIiwKICAgICAgIHBsb3QgPSBsYXN0X3Bsb3QoKSwKICAgICAgIGRldmljZSA9ICJzdmciLAogICAgICAgd2lkdGggPSAxMCwKICAgICAgIGhlaWdodCA9IDUsCiAgICAgICB1bml0cyA9ICJpbiIpCmBgYAoKQ2xvc2UgdXAgb2YgY29uc2lzdGVudGx5IHVwcmVndWxhdGVkIGdlbmVzCgpgYGB7ciwgZmlndXJlLndpZHRoID0gMTAsIGZpZ3VyZS5oZWlnaHQgPSA1fQojIEVuZCB0aGUgcGxvdCBiZXR3ZWVuICJjYXRlZ29yaWVzIiBvZiBnZW5lcywgaW5jbHVkaW5nIGxlc3MgdGhhbiAxMDAgZ2VuZXMgdG90YWwKZW5kX2luZGV4ID0gMQpmb3IoaSBpbiAxOjEwMCkgewogIGlmIChwbG90X2RhdGEkb3JkZXJbaV0gIT0gcGxvdF9kYXRhJG9yZGVyW2krMV0pIHsKICAgIGVuZF9pbmRleCA9IGkKICB9Cn0KCnBsb3RfZGF0YV91cCA9IHBsb3RfZGF0YVsxOmVuZF9pbmRleCxdCnBsb3RfZGF0YV91cCRnZW5lID0gcm93bmFtZXMocGxvdF9kYXRhX3VwKQoKcHJpbnQocGxvdF9kYXRhX3VwKQoKZ2dwbG90KHBsb3RfZGF0YV91cCkgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSAxKSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZW9tX3RleHQoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gMSwgbGFiZWwgPSBnZW5lKSwgc2l6ZSA9IDIsIGNvbG9yID0gImJsYWNrIiwgYW5nbGUgPSA5MCkgKwogICMgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gLTEgKiBudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkLCBmaWxsID0gImRvd25yZWd1bGF0aW9uIiwgd2lkdGggPSBjb3VudCksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgIyBnZW9tX2JhcihhZXMoeCA9IGNlbnRlcl9wbGFjZW1lbnQsIHkgPSBudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCwgZmlsbCA9ICJ1cHJlZ3VsYXRpb24iLCB3aWR0aCA9IGNvdW50KSwgc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtbWF4KHBsb3RfZGF0YV91cCRudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkKSAtMSwgbWF4KHBsb3RfZGF0YV91cCRudW1fcGFpcnNfc2lnX3VwX3JlZ3VsYXRlZCkpICsxKSArCiAgIyBTZXQgeS1heGlzIGxpbWl0cwogICMgY29vcmRfZmxpcCgpICsgICMgRmxpcCB0aGUgY29vcmRpbmF0ZXMgdG8gY3JlYXRlIGEgc2lkZXdheXMgcGxvdAogIHRoZW1lX21pbmltYWwoKSArICAjIEFwcGx5IGEgbWluaW1hbCB0aGVtZQogIGxhYnMoeCA9ICJHZW5lcyIsIHkgPSAiIyByZXNpc3RhbnQgY2VsbGxpbmVzIHNob3dpbmcgc2lnbmlmaWNhbnQgcmVndWxhdGlvbiBvZiBnZW5lIikgKyAjIFNldCBheGlzIGxhYmVscwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInVwcmVndWxhdGlvbiIgPSAiZ3JlZW4iLCAiZG93bnJlZ3VsYXRpb24iID0gInJlZCIpKSArICMgU2V0IGZpbGwgY29sb3JzCiAgdGhlbWVfY2xhc3NpYygpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5tYWpvci55ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICAgICMgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAjIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgZ2d0aXRsZSgnWm9vbWluZyBpbiBvbiBsZWZ0IGhhbmQgc2lkZSAoZXh0cmVtZWx5IGNvbnNpc3RlbnRseSB1cHJlZ3VsYXRlZCBnZW5lcyknKQogICMgZ2d0aXRsZSgnR2VuZXMgZXh0cmVtZWx5IGNvbnNpc3RlbnRseSB1cHJlZ3VsYXRlZCBpbiByZXNpc3RhbnQgY2VsbGxpbmVzJykKCmdnc2F2ZSgiZGVzZXEvb3V0cHV0L2NvbnNpc3RlbnRseV91cHJlZ3VsYXRlZF9nZW5lc19hbGxfY2VsbGxpbmVzLnN2ZyIsCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCksCiAgICAgICBkZXZpY2UgPSAic3ZnIiwKICAgICAgIHdpZHRoID0gMTAsCiAgICAgICBoZWlnaHQgPSA1LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCkNsb3NlIHVwIG9mIGNvbnNpc3RlbnRseSBkb3ducmVndWxhdGVkIGdlbmVzCgpgYGB7ciwgZmlndXJlLndpZHRoID0gMTAsIGZpZ3VyZS5oZWlnaHQgPSA1fQojIEVuZCB0aGUgcGxvdCBiZXR3ZWVuICJjYXRlZ29yaWVzIiBvZiBnZW5lcywgaW5jbHVkaW5nIGxlc3MgdGhhbiAxMDAgZ2VuZXMgdG90YWwKc3RhcnRfaW5kZXggPSAxCmZvcihpIGluIG5yb3cocGxvdF9kYXRhKToobnJvdyhwbG90X2RhdGEpIC0gOTkpKSB7CiAgaWYgKHBsb3RfZGF0YSRvcmRlcltpXSAhPSBwbG90X2RhdGEkb3JkZXJbaS0xXSkgewogICAgc3RhcnRfaW5kZXggPSBpCiAgfQp9CgpwbG90X2RhdGFfZG93biA9IHBsb3RfZGF0YVtzdGFydF9pbmRleDpucm93KHBsb3RfZGF0YSksXQpwbG90X2RhdGFfZG93biRjZW50ZXJfcGxhY2VtZW50ID0gcGxvdF9kYXRhX2Rvd24kY2VudGVyX3BsYWNlbWVudCAtIHN0YXJ0X2luZGV4ICsgMSAjIFN0YXJ0IGF0IDAKcGxvdF9kYXRhX2Rvd24kZ2VuZSA9IHJvd25hbWVzKHBsb3RfZGF0YV9kb3duKQoKcHJpbnQocGxvdF9kYXRhX2Rvd24pCgpnZ3Bsb3QocGxvdF9kYXRhX2Rvd24pICsKICBnZW9tX2JhcihhZXMoeCA9IGNlbnRlcl9wbGFjZW1lbnQsIHkgPSAtMSAqIG51bV9wYWlyc19zaWdfZG93bl9yZWd1bGF0ZWQsIGZpbGwgPSAiZG93bnJlZ3VsYXRpb24iLCB3aWR0aCA9IDEpLCBzdGF0ID0gImlkZW50aXR5IikgKwogIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkLCBmaWxsID0gInVwcmVndWxhdGlvbiIsIHdpZHRoID0gMSksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgZ2VvbV90ZXh0KGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IDIsIGxhYmVsID0gZ2VuZSksIHNpemUgPSAyLCBjb2xvciA9ICJibGFjayIsIGFuZ2xlID0gOTApICsKICAjIGdlb21fYmFyKGFlcyh4ID0gY2VudGVyX3BsYWNlbWVudCwgeSA9IC0xICogbnVtX3BhaXJzX3NpZ19kb3duX3JlZ3VsYXRlZCwgZmlsbCA9ICJkb3ducmVndWxhdGlvbiIsIHdpZHRoID0gY291bnQpLCBzdGF0ID0gImlkZW50aXR5IikgKwogICMgZ2VvbV9iYXIoYWVzKHggPSBjZW50ZXJfcGxhY2VtZW50LCB5ID0gbnVtX3BhaXJzX3NpZ191cF9yZWd1bGF0ZWQsIGZpbGwgPSAidXByZWd1bGF0aW9uIiwgd2lkdGggPSBjb3VudCksIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLW1heChwbG90X2RhdGFfZG93biRudW1fcGFpcnNfc2lnX2Rvd25fcmVndWxhdGVkKSAtMSwgbWF4KHBsb3RfZGF0YV9kb3duJG51bV9wYWlyc19zaWdfdXBfcmVndWxhdGVkKSkgKzEpICsKICAjIFNldCB5LWF4aXMgbGltaXRzCiAgIyBjb29yZF9mbGlwKCkgKyAgIyBGbGlwIHRoZSBjb29yZGluYXRlcyB0byBjcmVhdGUgYSBzaWRld2F5cyBwbG90CiAgdGhlbWVfbWluaW1hbCgpICsgICMgQXBwbHkgYSBtaW5pbWFsIHRoZW1lCiAgbGFicyh4ID0gIkdlbmVzIiwgeSA9ICIjIHJlc2lzdGFudCBjZWxsbGluZXMgc2hvd2luZyBzaWduaWZpY2FudCByZWd1bGF0aW9uIG9mIGdlbmUiKSArICMgU2V0IGF4aXMgbGFiZWxzCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygidXByZWd1bGF0aW9uIiA9ICJncmVlbiIsICJkb3ducmVndWxhdGlvbiIgPSAicmVkIikpICsgIyBTZXQgZmlsbCBjb2xvcnMKICB0aGVtZV9jbGFzc2ljKCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgcGFuZWwuZ3JpZC5taW5vci55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgICAgIyBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICMgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBnZ3RpdGxlKCdab29taW5nIGluIG9uIHJpZ2h0IGhhbmQgc2lkZSAoZXh0cmVtZWx5IGNvbnNpc3RlbnRseSBkb3ducmVndWxhdGVkIGdlbmVzKScpCiAgIyBnZ3RpdGxlKCdHZW5lcyBjb25zaXN0ZW50bHkgdXByZWd1bGF0ZWQgaW4gcmVzaXN0YW50IGNlbGxsaW5lcycpCgpnZ3NhdmUoImRlc2VxL291dHB1dC9jb25zaXN0ZW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lc19hbGxfY2VsbGxpbmVzLnN2ZyIsCiAgICAgICBwbG90ID0gbGFzdF9wbG90KCksCiAgICAgICBkZXZpY2UgPSAic3ZnIiwKICAgICAgIHdpZHRoID0gMTAsCiAgICAgICBoZWlnaHQgPSA1LAogICAgICAgdW5pdHMgPSAiaW4iKQpgYGAKCiMjIFVwc2V0IHBsb3RzCgpVcHNldCBwbG90cyBhcmUgYWtpbiB0byBhIFZlbm4gZGlhZ3JhbSBhbmQgc2hvdyB0aGUgb3ZlcmxhcCBvZiBnZW5lcyB0aGF0IHdlcmUgc2lnbmlmaWNhbnRseSB1cCBhbmQgZG93biByZWd1bGF0ZWQgYnkgZWFjaCByZXNpc3RhbnQgY2VsbGxpbmUgKGFzIGNvbXBhcmVkIHRvIGl0cyByZXNwZWN0aXZlIGNvbnRyb2wpLgoKTm90ZSB0aGF0IHRoZXNlIHVwc2V0IHBsb3RzIHVzZSB0aGUgImRpc3RpbmN0IiBtb2RlIChtZWFuaW5nIHRoYXQgZWFjaCByZXByZXNlbnRlZCBpbnRlcnNlY3Rpb24gY29uc2lzdHMgb2YgdGhlICJpbnRlcnNlY3Rpb24gZWxlbWVudHMgdGhhdCBiZWxvbmcgdG8gdGhlIHNldHMgZGVmaW5pbmcgdGhlIGludGVyc2VjdGlvbiBidXQgbm90IHRvIGFueSBvdGhlciBzZXQiKS4KClRPRE86IHdoaWNoIG9mIHRoZSBodW5kcmVkcyBvZiBwb3NzaWJsZSBjb21iaW5hdGlvbnMgdG8gc2hvdz8gRm9yIG5vdyBJJ20gc2VwYXJhdGluZyB1cHJlZ3VsYXRpb24gYW5kIGRvd25yZWd1bGF0aW9uIHRvIGRlY3JlYXNlIHRoZSBudW1iZXIgb2YgcG9zc2libGUgaW50ZXJzZWN0aW9ucyBieSBhIGxvdCwgYnV0IEknbSBzdGlsbCBvbmx5IHNob3dpbmcgdGhlIHRvcCA1MCBpbnRlcnNlY3Rpb25zIChyYW5rZWQgYnkgZGVncmVlIC0tIGkuZS4gbnVtYmVyIG9mIGNlbGxMaW5lcyB0aGF0IHNoYXJlIHRoYXQgdXAgb3IgZG93bnJlZ3VsYXRlZCBnZW5lKS4KCiMjIyBDb21iaW5lZCB1cCBhbmQgZG93biBnZW5lIHVwc2V0IHBsb3QKCmBgYHtyIGNvbWJpbmVkIHVwc2V0IHBsb3R9CmNvbWJpbmVkVXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpKQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHNwbGl0IDwtIHN0cnNwbGl0KHBhaXIsICJfdnNfIikKICBjb250IDwtIHNwbGl0W1sxXV1bMl0KICBleHAgPC0gc3BsaXRbWzFdXVsxXQogIGZvciAoZGlyIGluIGMoInVwIiwgImRvd24iKSkgewogICAgcmVndWxhdGVkR2VuZXNGaWxlID0gc3RyX2ludGVycCgiZGVzZXEvb3V0cHV0LyR7ZXhwfV92c18ke2NvbnR9X3NpZ25pZmljYW50bHlfJHtkaXJ9cmVndWxhdGVkX2dlbmVzLmNzdiIpCiAgICByZWd1bGF0ZWRHZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHJlZ3VsYXRlZEdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAgIGtleSA9IHN0cl9pbnRlcnAoIiR7ZXhwfV9zaWdfJHtkaXJ9cmVnX2dlbmVzIikKICAgIGNvbWJpbmVkVXBzZXRMaXN0SW5wdXRbW2tleV1dID0gcm93bmFtZXMocmVndWxhdGVkR2VuZXMpCiAgfQp9Cgp1cHNldChmcm9tTGlzdChjb21iaW5lZFVwc2V0TGlzdElucHV0KSwgbnNldHMgPSAxNCwgbmludGVyc2VjdHMgPSA1MCwgb3JkZXIuYnkgPSAiZGVncmVlIikKYGBgCgojIyMgVXByZWd1bGF0ZWQgZ2VuZSB1cHNldCBwbG90CgpTaG93cyBvbmx5IHRoZSB1cHJlZ3VsYXRlZCBnZW5lcy4KCmBgYHtyIHVwcmVnIHVwc2V0IHBsb3R9CnVwcmVnVXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpKQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHNwbGl0IDwtIHN0cnNwbGl0KHBhaXIsICJfdnNfIikKICBjb250IDwtIHNwbGl0W1sxXV1bMl0KICBleHAgPC0gc3BsaXRbWzFdXVsxXQogIAogIHVwR2VuZXNGaWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke2V4cH1fdnNfJHtjb250fV9zaWduaWZpY2FudGx5X3VwcmVndWxhdGVkX2dlbmVzLmNzdiIpCiAgdXBHZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHVwR2VuZXNGaWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIGtleSA9IHN0cl9pbnRlcnAoIiR7ZXhwfV9zaWdfdXByZWdfZ2VuZXMiKQogIHVwcmVnVXBzZXRMaXN0SW5wdXRbW2tleV1dID0gcm93bmFtZXModXBHZW5lcykKfQoKdXBzZXQoZnJvbUxpc3QodXByZWdVcHNldExpc3RJbnB1dCksIG5zZXRzID0gNywgbmludGVyc2VjdHMgPSA1MCwgb3JkZXIuYnkgPSAiZGVncmVlIikKYGBgCgpPdXRwdXQgdGhlIGxpc3RzIG9mIGdlbmVzIGNvcnJlc3BvbmRpbmcgdG8gZWFjaCBjb2x1bW4gb2YgdGhlIHVwc2V0IHBsb3QKCmBgYHtyIHVwcmVndWxhdGVkIHVwc2V0IHBsb3QgZGF0YX0KZnJvbUxpc3QodXByZWdVcHNldExpc3RJbnB1dCkKdXByZWdVcHNldFBsb3RHZW5lcy5kZiA8LSBnZXRVcHNldFBsb3REYXRhKHVwcmVnVXBzZXRMaXN0SW5wdXQpCndyaXRlLmNzdih1cHJlZ1Vwc2V0UGxvdEdlbmVzLmRmLCBmaWxlID0gImRlc2VxL291dHB1dC91cHNldF9wbG90X2xpc3RzX3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfZ2VuZXMuY3N2IikKdXByZWdVcHNldFBsb3RHZW5lcy5kZgpgYGAKCiMjIyBEb3ducmVndWxhdGVkIGdlbmUgdXBzZXQgcGxvdAoKU2hvd3Mgb25seSB0aGUgZG93bnJlZ3VsYXRlZCBnZW5lcy4KCmBgYHtyIGRvd25yZWcgdXBzZXQgcGxvdH0KZG93bnJlZ1Vwc2V0TGlzdElucHV0IDwtIHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzKSkKCmZpcnN0X3BhaXIgPSBUUlVFCmNvbnNpc3RlbnRfZG93bl9nZW5lcyA9IGMoKQoKZm9yIChwYWlyIGluIHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpIHsKICBzcGxpdCA8LSBzdHJzcGxpdChwYWlyLCAiX3ZzXyIpCiAgZXhwIDwtIHNwbGl0W1sxXV1bMV0KICAKICBkb3duR2VuZXNGaWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiKQogIGRvd25HZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KGRvd25HZW5lc0ZpbGUsIHNlcCA9ICIsIiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkpCiAga2V5ID0gc3RyX2ludGVycCgiJHtleHB9X3NpZ19kb3ducmVnX2dlbmVzIikKICBkb3ducmVnVXBzZXRMaXN0SW5wdXRbW2tleV1dID0gcm93bmFtZXMoZG93bkdlbmVzKQogIAogIGlmIChmaXJzdF9wYWlyID09IFRSVUUpIHsKICAgIGNvbnNpc3RlbnRfZG93bl9nZW5lcyA9IHJvd25hbWVzKGRvd25HZW5lcykKICAgIGZpcnN0X3BhaXIgPSBGQUxTRQogIH0gZWxzZSB7CiAgICBjb25zaXN0ZW50X2Rvd25fZ2VuZXMgPSBpbnRlcnNlY3QoY29uc2lzdGVudF9kb3duX2dlbmVzLCByb3duYW1lcyhkb3duR2VuZXMpKQogIH0KfQoKdXBzZXQoZnJvbUxpc3QoZG93bnJlZ1Vwc2V0TGlzdElucHV0KSwgbnNldHMgPSA3LCBuaW50ZXJzZWN0cyA9IDUwLCBvcmRlci5ieSA9ICJkZWdyZWUiKQoKcHJpbnQoIkdlbmVzIGRvd25yZWd1bGF0ZWQgaW4gYWxsIHNlbnNpdGl2ZS9yZXNpc3RhbnQgcGFpcnMiKQpwcmludChjb25zaXN0ZW50X2Rvd25fZ2VuZXMpCmBgYAoKT3V0cHV0IHRoZSBsaXN0cyBvZiBnZW5lcyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggY29sdW1uIG9mIHRoZSB1cHNldCBwbG90CgpgYGB7ciBkb3ducmVndWxhdGVkIHVwc2V0IHBsb3QgZGF0YSBpc29saW5lc30KZG93bnJlZ1Vwc2V0UGxvdEdlbmVzLmRmIDwtIGdldFVwc2V0UGxvdERhdGEoZG93bnJlZ1Vwc2V0TGlzdElucHV0KQp3cml0ZS5jc3YoZG93bnJlZ1Vwc2V0UGxvdEdlbmVzLmRmLCBmaWxlID0gImRlc2VxL291dHB1dC91cHNldF9wbG90X2lzb2xpbmVzX2xpc3RzX3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiKQpkb3ducmVnVXBzZXRQbG90R2VuZXMuZGYKYGBgCgoKCiMjIyBVcHNldCBwbG90cyBhY3Jvc3MgaXNvbGluZXMKCkdlbmUgaW5jbHVkZWQgaW4gaXNvbGluZSBpZiBpdCBpcyBzaWcgdXByZWd1bGF0ZWQgaW4gQU5ZIG9mIHRoZSBjZWxsIGxpbmVzCgpgYGB7ciB1cHJlZyB1cHNldCBieSBpc29saW5lIGFueX0KdXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKGlzb2xpbmVzKSkKCmZvciAoaXNvbGluZSBpbiB1bmlxdWUoaXNvbGluZXMkaXNvbGluZSkpIHsKICBwYWlycyA9IGlzb2xpbmVzW2lzb2xpbmVzJGlzb2xpbmUgPT0gaXNvbGluZSwgInBhaXIiXQogIHVwR2VuZXMgPC0gbGlzdCgpCiAgCiAgZm9yIChwYWlyIGluIHBhaXJzKSB7CiAgICB1cEdlbmVzRmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfZ2VuZXMuY3N2IikKICAgIG5ld1VwR2VuZXMgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdih1cEdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAgIHVwR2VuZXMgPC0gdW5pcXVlKGModXBHZW5lcywgcm93bmFtZXMobmV3VXBHZW5lcykpKQogIH0KICAKICB1cHNldExpc3RJbnB1dFtbaXNvbGluZV1dID0gdXBHZW5lcwp9Cgp1cHNldChmcm9tTGlzdCh1cHNldExpc3RJbnB1dCksIG5zZXRzID0gNywgbmludGVyc2VjdHMgPSA1MCwgb3JkZXIuYnkgPSAiZGVncmVlIikKCiMgT3V0cHV0IHRoZSBsaXN0cyBvZiBnZW5lcyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggY29sdW1uIG9mIHRoZSB1cHNldCBwbG90CnVwcmVnVXBzZXRQbG90R2VuZXMuZGYgPC0gZ2V0VXBzZXRQbG90RGF0YSh1cHNldExpc3RJbnB1dCkKd3JpdGUuY3N2KHVwcmVnVXBzZXRQbG90R2VuZXMuZGYsIGZpbGUgPSAiZGVzZXEvb3V0cHV0L3Vwc2V0X3Bsb3RfaXNvbGluZXNfbGlzdHNfc2lnbmlmaWNhbnRseV91cHJlZ3VsYXRlZF9nZW5lc19hbnlfc3VibGluZXMuY3N2IikKdXByZWdVcHNldFBsb3RHZW5lcy5kZgpgYGAKCkdlbmUgaW5jbHVkZWQgaW4gaXNvbGluZSBpZiBpdCBpcyBzaWcgdXByZWd1bGF0ZWQgaW4gQUxMIG9mIHRoZSBjZWxsIGxpbmVzCgpgYGB7ciB1cHJlZyB1cHNldCBieSBpc29saW5lIGFsbH0KdXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKGlzb2xpbmVzKSkKCmZvciAoaXNvbGluZSBpbiB1bmlxdWUoaXNvbGluZXMkaXNvbGluZSkpIHsKICBwYWlycyA9IGlzb2xpbmVzW2lzb2xpbmVzJGlzb2xpbmUgPT0gaXNvbGluZSwgInBhaXIiXQogIAogIHBhaXIgPSBwYWlyc1sxXQogIHVwR2VuZXNGaWxlID0gc3RyX2ludGVycCgiZGVzZXEvb3V0cHV0LyR7cGFpcn1fc2lnbmlmaWNhbnRseV91cHJlZ3VsYXRlZF9nZW5lcy5jc3YiKQogIHVwR2VuZXMgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdih1cEdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICB1cEdlbmVzIDwtIHJvd25hbWVzKHVwR2VuZXMpCiAgCiAgaWYgKGxlbmd0aChwYWlycykgPiAxKSB7CiAgICBmb3IgKHBhaXIgaW4gcGFpcnNbMjpsZW5ndGgocGFpcnMpXSkgewogICAgICB1cEdlbmVzRmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfZ2VuZXMuY3N2IikKICAgICAgbmV3VXBHZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHVwR2VuZXNGaWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogICAgICB1cEdlbmVzIDwtIGludGVyc2VjdCh1cEdlbmVzLCByb3duYW1lcyhuZXdVcEdlbmVzKSkKICAgIH0KICB9CiAgCiAgdXBzZXRMaXN0SW5wdXRbW2lzb2xpbmVdXSA9IHVwR2VuZXMKfQoKdXBzZXQoZnJvbUxpc3QodXBzZXRMaXN0SW5wdXQpLCBuc2V0cyA9IDcsIG5pbnRlcnNlY3RzID0gNTAsIG9yZGVyLmJ5ID0gImRlZ3JlZSIpCgojIE91dHB1dCB0aGUgbGlzdHMgb2YgZ2VuZXMgY29ycmVzcG9uZGluZyB0byBlYWNoIGNvbHVtbiBvZiB0aGUgdXBzZXQgcGxvdAp1cHJlZ1Vwc2V0UGxvdEdlbmVzLmRmIDwtIGdldFVwc2V0UGxvdERhdGEodXBzZXRMaXN0SW5wdXQpCndyaXRlLmNzdih1cHJlZ1Vwc2V0UGxvdEdlbmVzLmRmLCBmaWxlID0gImRlc2VxL291dHB1dC91cHNldF9wbG90X2lzb2xpbmVzX2xpc3RzX3NpZ25pZmljYW50bHlfdXByZWd1bGF0ZWRfZ2VuZXNfYWxsX3N1YmxpbmVzLmNzdiIpCnVwcmVnVXBzZXRQbG90R2VuZXMuZGYKYGBgCgpHZW5lIGluY2x1ZGVkIGluIGlzb2xpbmUgaWYgaXQgaXMgc2lnIGRvd25yZWd1bGF0ZWQgaW4gQU5ZIG9mIHRoZSBjZWxsIGxpbmVzCgpgYGB7ciBkb3ducmVnIHVwc2V0IGJ5IGlzb2xpbmUgYW55fQp1cHNldExpc3RJbnB1dCA8LSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgoaXNvbGluZXMpKQoKZm9yIChpc29saW5lIGluIHVuaXF1ZShpc29saW5lcyRpc29saW5lKSkgewogIHBhaXJzID0gaXNvbGluZXNbaXNvbGluZXMkaXNvbGluZSA9PSBpc29saW5lLCAicGFpciJdCiAgZG93bkdlbmVzIDwtIGxpc3QoKQogIAogIGZvciAocGFpciBpbiBwYWlycykgewogICAgZG93bkdlbmVzRmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiKQogICAgbmV3RG93bkdlbmVzIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5jc3YoZG93bkdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAgIGRvd25HZW5lcyA8LSB1bmlxdWUoYyhkb3duR2VuZXMsIHJvd25hbWVzKG5ld0Rvd25HZW5lcykpKQogIH0KICAKICB1cHNldExpc3RJbnB1dFtbaXNvbGluZV1dID0gZG93bkdlbmVzCn0KCnVwc2V0KGZyb21MaXN0KHVwc2V0TGlzdElucHV0KSwgbnNldHMgPSA3LCBuaW50ZXJzZWN0cyA9IDUwLCBvcmRlci5ieSA9ICJkZWdyZWUiKQoKIyBPdXRwdXQgdGhlIGxpc3RzIG9mIGdlbmVzIGNvcnJlc3BvbmRpbmcgdG8gZWFjaCBjb2x1bW4gb2YgdGhlIHVwc2V0IHBsb3QKZG93bnJlZ1Vwc2V0UGxvdEdlbmVzLmRmIDwtIGdldFVwc2V0UGxvdERhdGEodXBzZXRMaXN0SW5wdXQpCndyaXRlLmNzdihkb3ducmVnVXBzZXRQbG90R2VuZXMuZGYsIGZpbGUgPSAiZGVzZXEvb3V0cHV0L3Vwc2V0X3Bsb3RfaXNvbGluZXNfbGlzdHNfc2lnbmlmaWNhbnRseV9kb3ducmVndWxhdGVkX2dlbmVzX2FueV9zdWJsaW5lcy5jc3YiKQpkb3ducmVnVXBzZXRQbG90R2VuZXMuZGYKYGBgCgpHZW5lIGluY2x1ZGVkIGluIGlzb2xpbmUgaWYgaXQgaXMgc2lnIGRvd25yZWd1bGF0ZWQgaW4gQUxMIG9mIHRoZSBjZWxsIGxpbmVzCgpgYGB7ciBkb3ducmVnIHVwc2V0IGJ5IGlzb2xpbmUgYWxsfQp1cHNldExpc3RJbnB1dCA8LSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgoaXNvbGluZXMpKQoKZm9yIChpc29saW5lIGluIHVuaXF1ZShpc29saW5lcyRpc29saW5lKSkgewogIHBhaXJzID0gaXNvbGluZXNbaXNvbGluZXMkaXNvbGluZSA9PSBpc29saW5lLCAicGFpciJdCiAgCiAgcGFpciA9IHBhaXJzWzFdCiAgZG93bkdlbmVzRmlsZSA9IHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke3BhaXJ9X3NpZ25pZmljYW50bHlfZG93bnJlZ3VsYXRlZF9nZW5lcy5jc3YiKQogIGRvd25HZW5lcyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KGRvd25HZW5lc0ZpbGUsIHNlcCA9ICIsIiwgaGVhZGVyID0gVFJVRSwgcm93Lm5hbWVzID0gMSkpCiAgZG93bkdlbmVzIDwtIHJvd25hbWVzKGRvd25HZW5lcykKICAKICBpZiAobGVuZ3RoKHBhaXJzKSA+IDEpIHsKICAgIGZvciAocGFpciBpbiBwYWlyc1syOmxlbmd0aChwYWlycyldKSB7CiAgICAgIGRvd25HZW5lc0ZpbGUgPSBzdHJfaW50ZXJwKCJkZXNlcS9vdXRwdXQvJHtwYWlyfV9zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfZ2VuZXMuY3N2IikKICAgICAgbmV3RG93bkdlbmVzIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5jc3YoZG93bkdlbmVzRmlsZSwgc2VwID0gIiwiLCBoZWFkZXIgPSBUUlVFLCByb3cubmFtZXMgPSAxKSkKICAgICAgZG93bkdlbmVzIDwtIGludGVyc2VjdChkb3duR2VuZXMsIHJvd25hbWVzKG5ld0Rvd25HZW5lcykpCiAgICB9CiAgfQogIAogIHVwc2V0TGlzdElucHV0W1tpc29saW5lXV0gPSBkb3duR2VuZXMKfQoKdXBzZXQoZnJvbUxpc3QodXBzZXRMaXN0SW5wdXQpLCBuc2V0cyA9IDcsIG5pbnRlcnNlY3RzID0gNTAsIG9yZGVyLmJ5ID0gImRlZ3JlZSIpCgojIE91dHB1dCB0aGUgbGlzdHMgb2YgZ2VuZXMgY29ycmVzcG9uZGluZyB0byBlYWNoIGNvbHVtbiBvZiB0aGUgdXBzZXQgcGxvdApkb3ducmVnVXBzZXRQbG90R2VuZXMuZGYgPC0gZ2V0VXBzZXRQbG90RGF0YSh1cHNldExpc3RJbnB1dCkKd3JpdGUuY3N2KGRvd25yZWdVcHNldFBsb3RHZW5lcy5kZiwgZmlsZSA9ICJkZXNlcS9vdXRwdXQvdXBzZXRfcGxvdF9pc29saW5lc19saXN0c19zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfZ2VuZXNfYWxsX3N1YmxpbmVzLmNzdiIpCnVwcmVnVXBzZXRQbG90R2VuZXMuZGYKYGBgCgoKIyMjIFVwcmVndWxhdGVkIHBhdGh3YXkgdXBzZXQgcGxvdAoKU2hvd3Mgb25seSB0aGUgdXByZWd1bGF0ZWQgcGF0aHdheXMuCgpgYGB7ciB1cHJlZyBwYXRoIHVwc2V0IHBsb3R9CnVwcmVnVXBzZXRMaXN0SW5wdXQgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpKQpmb3IgKHBhaXIgaW4gc2Vuc2l0aXZlX3Jlc2lzdGFudF9wYWlycykgewogIHNwbGl0IDwtIHN0cnNwbGl0KHBhaXIsICJfdnNfIikKICBjb250IDwtIHNwbGl0W1sxXV1bMl0KICBleHAgPC0gc3BsaXRbWzFdXVsxXQogIAogIHVwUGF0aHdheXNGaWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke2V4cH1fdnNfJHtjb250fV9zaWduaWZpY2FudGx5X3VwcmVndWxhdGVkX3BhdGh3YXlzLmNzdiIpCiAgdXBQYXRod2F5cyA8LSBhcy5kYXRhLmZyYW1lKHJlYWQuY3N2KHVwUGF0aHdheXNGaWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIGtleSA9IHN0cl9pbnRlcnAoIiR7ZXhwfV9zaWdfdXByZWdfcGF0aHdheXMiKQogIHVwcmVnVXBzZXRMaXN0SW5wdXRbW2tleV1dID0gdXBQYXRod2F5cyREZXNjcmlwdGlvbgp9Cgp1cHNldChmcm9tTGlzdCh1cHJlZ1Vwc2V0TGlzdElucHV0KSwgbnNldHMgPSA3LCBuaW50ZXJzZWN0cyA9IDUwLCBvcmRlci5ieSA9ICJkZWdyZWUiKQpgYGAKCk91dHB1dCB0aGUgbGlzdHMgb2YgcGF0aHdheXMgY29ycmVzcG9uZGluZyB0byBlYWNoIGNvbHVtbiBvZiB0aGUgdXBzZXQgcGxvdAoKYGBge3IgdXByZWd1bGF0ZWQgcGF0aCB1cHNldCBwbG90IGRhdGF9CmZyb21MaXN0KHVwcmVnVXBzZXRMaXN0SW5wdXQpCnVwcmVnVXBzZXRQbG90UGF0aHdheXMuZGYgPC0gZ2V0VXBzZXRQbG90RGF0YSh1cHJlZ1Vwc2V0TGlzdElucHV0KQp3cml0ZS5jc3YodXByZWdVcHNldFBsb3RQYXRod2F5cy5kZiwgZmlsZSA9ICJkZXNlcS9vdXRwdXQvdXBzZXRfcGxvdF9saXN0c19zaWduaWZpY2FudGx5X3VwcmVndWxhdGVkX3BhdGh3YXlzLmNzdiIpCnVwcmVnVXBzZXRQbG90UGF0aHdheXMuZGYKYGBgCgojIyMgRG93bnJlZ3VsYXRlZCBwYXRod2F5IHVwc2V0IHBsb3QKClNob3dzIG9ubHkgdGhlIGRvd25yZWd1bGF0ZWQgcGF0aHdheXMuCgpgYGB7ciBkb3ducmVnIHBhdGggdXBzZXQgcGxvdH0KZG93bnJlZ1Vwc2V0TGlzdElucHV0IDwtIHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChzZW5zaXRpdmVfcmVzaXN0YW50X3BhaXJzKSkKZm9yIChwYWlyIGluIHNlbnNpdGl2ZV9yZXNpc3RhbnRfcGFpcnMpIHsKICBzcGxpdCA8LSBzdHJzcGxpdChwYWlyLCAiX3ZzXyIpCiAgY29udCA8LSBzcGxpdFtbMV1dWzJdCiAgZXhwIDwtIHNwbGl0W1sxXV1bMV0KICAKICBkb3duUGF0aHdheXNGaWxlIDwtIHN0cl9pbnRlcnAoImRlc2VxL291dHB1dC8ke2V4cH1fdnNfJHtjb250fV9zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfcGF0aHdheXMuY3N2IikKICBkb3duUGF0aHdheXMgPC0gYXMuZGF0YS5mcmFtZShyZWFkLmNzdihkb3duUGF0aHdheXNGaWxlLCBzZXAgPSAiLCIsIGhlYWRlciA9IFRSVUUsIHJvdy5uYW1lcyA9IDEpKQogIGtleSA9IHN0cl9pbnRlcnAoIiR7ZXhwfV9zaWdfZG93bnJlZ19wYXRod2F5cyIpCiAgZG93bnJlZ1Vwc2V0TGlzdElucHV0W1trZXldXSA9IGRvd25QYXRod2F5cyREZXNjcmlwdGlvbgp9Cgp1cHNldChmcm9tTGlzdChkb3ducmVnVXBzZXRMaXN0SW5wdXQpLCBuc2V0cyA9IDcsIG5pbnRlcnNlY3RzID0gNTAsIG9yZGVyLmJ5ID0gImRlZ3JlZSIpCmBgYAoKT3V0cHV0IHRoZSBsaXN0cyBvZiBwYXRod2F5cyBjb3JyZXNwb25kaW5nIHRvIGVhY2ggY29sdW1uIG9mIHRoZSB1cHNldCBwbG90CgpgYGB7ciBkb3ducmVndWxhdGVkIHBhdGggdXBzZXQgcGxvdCBkYXRhfQpmcm9tTGlzdChkb3ducmVnVXBzZXRMaXN0SW5wdXQpCmRvd25yZWdVcHNldFBsb3RQYXRod2F5cy5kZiA8LSBnZXRVcHNldFBsb3REYXRhKGRvd25yZWdVcHNldExpc3RJbnB1dCkKd3JpdGUuY3N2KGRvd25yZWdVcHNldFBsb3RQYXRod2F5cy5kZiwgZmlsZSA9ICJkZXNlcS9vdXRwdXQvdXBzZXRfcGxvdF9saXN0c19zaWduaWZpY2FudGx5X2Rvd25yZWd1bGF0ZWRfcGF0aHdheXMuY3N2IikKZG93bnJlZ1Vwc2V0UGxvdFBhdGh3YXlzLmRmCmBgYAoKUmVhZCBpbiBwbGF0aW51bSBtZWNoYW5pc20gZGF0YQpgYGB7cn0KcGxhdGludW1HZW5lRGF0YSA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoImRlc2VxL3NwZWNpZmljLXBhdGh3YXlzL3BsYXRpbnVtLWxpc3QtYWxsLW1lY2hhbmlzbXMudHh0Iiwgc2VwID0gIlxuIiwgaGVhZGVyID0gVFJVRSkpCnJvdy5uYW1lcyhwbGF0aW51bUdlbmVEYXRhKSA8LSBwbGF0aW51bUdlbmVEYXRhWywxXQoKcGxhdGludW1HZW5lRGF0YQpgYGAKCiMjIFZpc3VhbGl6aW5nIGRpZmZlcmVudGlhbCBnZW5lcyBhY3Jvc3Mgc2Vuc2l0aXZlL3Jlc2lzdGFudCBwYWlycwoKU2hvd3MgZ2VuZSBleHByZXNzaW9uIGFjcm9zcyBzZW5zaXRpdmUvcmVzaXN0YW50IHBhaXJzLiBJbmNsdWRlcyBnZW5lcyB0aGF0IGFyZSBjb25zaXN0ZW50bHkgcmVndWxhdGVkIGluIGF0IGxlYXN0IDMgc2Vuc2l0aXZlL3Jlc2lzdGFudCBwYWlycy4gVXNlcyBhIG1lYW4tY2VudGVyZWQgYXBwcm9hY2guCgpgYGB7cn0KY29uc2lzdGVudGx5UmVndWxhdGVkR2VuZXMgPSByb3duYW1lcyhmdWxsX3Jlc3VsdHNfZ2VuZXNbZnVsbF9yZXN1bHRzX2dlbmVzJG51bV9wYWlyc19zaWdfcmVndWxhdGVkID4gMiwgXSkKCmRkcy5hbGwgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudG1hdHJpeC5hbGwsIGNvbERhdGEgPSBtZXRhZGF0YS5hbGwsIGRlc2lnbiA9IH4gQ2VsbExpbmUgKyBSZXBsaWNhdGUpCgphbm5vdGF0aW9uX2NvbCA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEoZGRzLmFsbClbLGMoIlJlc2lzdGFudCIsICJJc29MaW5lIildKQphbm5vdGF0aW9uX2NvbCRSZXNpc3RhbnQgPC0gYXMuZmFjdG9yKGFubm90YXRpb25fY29sJFJlc2lzdGFudCkKYW5ub3RhdGlvbl9jb2wkSXNvTGluZSA8LSBhcy5mYWN0b3IoYW5ub3RhdGlvbl9jb2wkSXNvTGluZSkKYW5ub3RhdGlvbl9jb2wgPC0gYXMuZGF0YS5mcmFtZShhbm5vdGF0aW9uX2NvbCkKCnNlbGVjdCA8LSBUUE0ubG9nW3Jvd25hbWVzKFRQTS5sb2cpICVpbiUgY29uc2lzdGVudGx5UmVndWxhdGVkR2VuZXMsXQoKbWF0IDwtIGFzLm1hdHJpeChzZWxlY3QpCm1hdCA8LSBtYXQgLSByb3dNZWFucyhtYXQpCnBoZWF0bWFwKG1hdCwKICBjbHVzdGVyX3Jvd3MgPSBUUlVFLAogIGNsdXN0ZXJfY29scyA9IEZBTFNFLAogIHNob3dfcm93bmFtZXMgPSBGQUxTRSwKICBsYWJlbHNfY29sID0gY29sbmFtZXMoc2VsZWN0KSwKICBhbm5vdGF0aW9uX2NvbCA9IGFubm90YXRpb25fY29sLAogIGxlZ2VuZCA9IFRSVUUsCiAgY2VsbHdpZHRoID0gNSwKICBmb250c2l6ZSA9IDUsCiAgZm9udHNpemVfcm93ID0gMiwKICBib3JkZXJfY29sb3I9TkEKKQpgYGA=